~/ru.blog.lis.im
← все записи

Деплой Hugo на VPS через GitHub Actions

· 4 мин чтения

Стек

  • Hugo + Tailwind CSS + PostCSS
  • GitHub Actions: build → rsync → VPS
  • VPS: Ubuntu + nginx + certbot (Let’s Encrypt)

Порядок настройки

1. Node.js зависимости

Если тема использует Tailwind/PostCSS — обязателен postcss-cli:

1cd ru.blog
2npm install --save-dev postcss-cli
3git add package.json package-lock.json
4git commit -m "add postcss-cli"

Без postcss-cli Hugo не найдёт бинарник postcss через npx и билд упадёт.


2. Deploy-пользователь на VPS

 1# Создать пользователя с ограниченным shell
 2useradd -m -s /bin/rbash deploy
 3
 4# Директория для сайта
 5mkdir -p /var/www/ru.blog.lis.im
 6chown deploy:deploy /var/www/ru.blog.lis.im
 7chmod 755 /var/www/ru.blog.lis.im
 8
 9# SSH-директория
10mkdir -p /home/deploy/.ssh
11chmod 700 /home/deploy/.ssh
12chown deploy:deploy /home/deploy/.ssh

Итоговые права:

drwxr-x---  deploy deploy  /home/deploy/
drwx------  deploy deploy  /home/deploy/.ssh/
drwxr-xr-x  deploy deploy  /var/www/ru.blog.lis.im/

nginx читает файлы сайта от своего пользователя (www-data), поэтому директория 755 — он сможет войти.
.ssh строго 700 — SSH демон отклонит ключ если права шире.


3. SSH-ключ

На локальной машине (не на VPS):

1ssh-keygen -t ed25519 -C "github-deploy-ru.blog" -f ~/.ssh/deploy_ru_blog
2# passphrase — оставить пустым, иначе GitHub Actions не сможет использовать ключ

Добавить публичный ключ на VPS:

1# Смотришь содержимое на локальной машине:
2cat ~/.ssh/deploy_ru_blog.pub
3
4# На VPS вставляешь в файл:
5nano /home/deploy/.ssh/authorized_keys

Формат строки в authorized_keys (без command= — он ломает rsync):

no-pty,no-agent-forwarding,no-port-forwarding,no-X11-forwarding ssh-ed25519 AAAA...ключ... github-deploy-ru.blog

Права на файл (строго, иначе SSH игнорирует):

1chmod 600 /home/deploy/.ssh/authorized_keys
2chown deploy:deploy /home/deploy/.ssh/authorized_keys

Проверить что всё правильно:

1ls -la /home/deploy/.ssh/
2# -rw------- deploy deploy authorized_keys  ← должно быть именно так

4. sshd_config

Проверить, нет ли ограничений для не-root пользователей:

1sshd -T -C user=deploy | grep authenticationmethods
2# Должно вернуть: authenticationmethods publickey

Если возвращает keyboard-interactive — добавить блок перед Match User *,!root в /etc/ssh/sshd_config:

Match User deploy
    AuthenticationMethods publickey
    PasswordAuthentication no
    KbdInteractiveAuthentication no

Применить:

1sshd -t && systemctl reload ssh
2# На Ubuntu сервис называется ssh, не sshd

5. GitHub Secrets

GitHub → репозиторий → Settings → Secrets and variables → Actions:

SecretЗначение
VPS_HOSTIP VPS
VPS_USERdeploy
VPS_SSH_KEYсодержимое ~/.ssh/deploy_ru_blog (приватный, с хедерами)
VPS_SSH_PORTпорт SSH (узнать: ss -tlnp | grep sshd)
VPS_DEPLOY_PATH/var/www/ru.blog.lis.im/

VPS_SSH_KEY — полное содержимое файла включая строки:

-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----

6. nginx — конфиг до certbot

Создать файл:

1nano /etc/nginx/sites-available/ru.blog.lis.im

Только HTTP, без SSL — certbot упадёт если SSL-блок уже есть, но сертификата ещё нет:

 1server {
 2    listen 80;
 3    listen [::]:80;
 4    server_name ru.blog.lis.im;
 5
 6    root /var/www/ru.blog.lis.im;
 7    index index.html;
 8
 9    location / {
10        try_files $uri $uri/ $uri.html =404;
11    }
12
13    error_page 404 /404.html;
14}

Активировать и проверить:

1ln -s /etc/nginx/sites-available/ru.blog.lis.im /etc/nginx/sites-enabled/
2nginx -t
3systemctl reload nginx

Убедиться что сайт открывается по HTTP перед запуском certbot:

1curl -I http://ru.blog.lis.im
2# HTTP/1.1 200 OK  ← должно быть 200

7. SSL через certbot

1apt install certbot python3-certbot-nginx
2certbot --nginx -d ru.blog.lis.im

Certbot сам перепишет конфиг, добавит HTTPS-блок и редирект с 80 на 443.

После этого добавить в SSL-блок кеширование статики (найти блок server { listen 443 ...}):

1    location ~* \.(css|js|woff2?|png|jpg|ico|svg)$ {
2        expires 1y;
3        add_header Cache-Control "public, immutable";
4    }
1nginx -t && systemctl reload nginx

8. Итоговый конфиг nginx после certbot

Certbot генерирует примерно такое (плюс твои дополнения):

 1server {
 2    listen 80;
 3    listen [::]:80;
 4    server_name ru.blog.lis.im;
 5    return 301 https://$host$request_uri;
 6}
 7
 8server {
 9    listen 443 ssl;
10    listen [::]:443 ssl;
11    server_name ru.blog.lis.im;
12
13    root /var/www/ru.blog.lis.im;
14    index index.html;
15
16    ssl_certificate     /etc/letsencrypt/live/ru.blog.lis.im/fullchain.pem;
17    ssl_certificate_key /etc/letsencrypt/live/ru.blog.lis.im/privkey.pem;
18    include /etc/letsencrypt/options-ssl-nginx.conf;
19    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
20
21    location ~* \.(css|js|woff2?|png|jpg|ico|svg)$ {
22        expires 1y;
23        add_header Cache-Control "public, immutable";
24    }
25
26    location / {
27        try_files $uri $uri/ $uri.html =404;
28    }
29
30    error_page 404 /404.html;
31}

Типичные проблемы

postcss not found using npx

Нет postcss-cli в зависимостях. Сам postcss пакет не даёт CLI.
npm install --save-dev postcss-cli, закоммитить package.json и package-lock.json.

remote_path can not be empty

Секрет VPS_DEPLOY_PATH не добавлен в GitHub Secrets.

Connection refused на порту 22

SSH слушает другой порт.
ss -tlnp | grep sshd, добавить найденный порт в VPS_SSH_PORT.

Permission denied (keyboard-interactive)

1. Неправильный владелец или права на authorized_keys
Файл должен принадлежать deploy:deploy с правами 600, не root.

2. command= директива в authorized_keys
command="rsync --server --daemon ." ломает GitHub Actions rsync.
Убрать command=..., оставить только no-pty,... ограничения.

3. sshd_config не разрешает publickey для deploy
Добавить Match User deploy блок с AuthenticationMethods publickey.
Проверить: sshd -T -C user=deploy | grep authenticationmethods

certbot: cannot load certificate

В nginx конфиге уже прописан SSL до получения сертификата.
→ Конфиг должен быть HTTP-only до запуска certbot. Убрать весь SSL, запустить certbot, он сам добавит.

sshd.service not found

На Ubuntu сервис называется ssh, не sshd.
systemctl reload ssh