Хостинг Hugo-блога на S3 Timeweb
Долгое время мой блог жил на VDS — nginx, certbot, systemd-сервисы, бекапы, короче полноценный сервер. В какой-то момент стало лень следить за ещё одной машиной и особенно меня подтолкнули проблемы с разными хостингами и я решил переехать на статический S3-хостинг.
Пост про то — как я переехал, с какими граблями столкнулся и что в итоге получилось.
Почему Timeweb S3
Почему именно Timeweb, а не Яндекс Облако или Selectel? Тут всё просто: у меня уже были там аккаунты, а S3 у них стоит копейки. Для статики — идеально.
Из коробки Timeweb S3 умеет раздавать статический сайт, выдаёт URL вида *.website.twcstorage.ru, поддерживает кастомные заголовки и политики.
Что нужно
Шаг 1: Создание бакета
- Заходим в панель Timeweb → S3 → Создать бакет
- Выбираем регион, имя бакета
- После создания открываем бакет → Настройки → Хостинг статического сайта → Включаем
- Получаем URL:
https://<id>.website.twcstorage.ru
Шаг 2: S3-ключи
В панели Timeweb идём в сервисные аккаунты → создаём ключ с доступом к бакету (на запись и чтение). Нам понадобятся:
Шаг 3: GitHub Actions
Деплой настроил через GitHub Actions. Workflow выглядит так:
name: Deploy blog to S3
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: peaceiris/actions-hugo@v3
with:
extended: true
- run: hugo --gc
- uses: jakejarvis/s3-sync-action@master
with:
args: --delete
env:
AWS_S3_BUCKET: ${{ secrets.AWS_BUCKET_NAME }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_ENDPOINT_URL: ${{ secrets.AWS_ENDPOINT_URL }}
Секреты (AWS_*) храню в настройках репозитория GitHub — Settings → Secrets and variables → Actions.
Шаг 4: Деплой
git push origin main
GitHub Actions собирает сайт и синхронизирует с бакетом. Всё, сайт на S3, никаких ssh и ручных заливок.
Шаг 5: Домены (не так всё просто)
blog.tatarinovms.ru → s3.twcstorage.ru blog.tatarinovms.space → s3.twcstorage.ru
Ждём, пока DNS обновится, открываем браузер… и получаем AccessDenied.
Подводный камень: кастомные домены не работают
Как оказалось, Timeweb S3 не поддерживает кастомные домены. Вообще. Только их *.website.twcstorage.ru. На момент написания поста ответ поддержки это подтвердил.
Вариант 1: CloudFlare Меняем NS на CloudFlare, он проксирует запросы на S3-эндпоинт и выдаёт свой SSL. VDS не нужен. Бесплатно. Но мы знаем как в некоторых регионах работает CloudFlare, скажем мягко - с перебоями.
Вариант 2: nginx reverse proxy Пришла простая идея, так как есть в загашнике VDS, то мы ставим nginx на VDS, он проксирует кастомные домены на S3 URL. SSL через certbot. VDS всё равно есть под другие задачи. Конфиг nginx:
server {
listen 80 default_server;
server_name blog.tatarinovms.ru blog.tatarinovms.space;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name blog.tatarinovms.ru blog.tatarinovms.space;
ssl_certificate /etc/letsencrypt/live/blog.tatarinovms.space/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.tatarinovms.space/privkey.pem;
location / {
proxy_pass https://id.website.twcstorage.ru;
proxy_ssl_verify off;
proxy_ssl_server_name on;
proxy_set_header Host id.website.twcstorage.ru;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
certbot --nginx -d blog.tatarinovms.ru -d blog.tatarinovms.space
Итоговая схема
Пуш в main → GitHub Actions → Hugo build → aws s3 sync → S3 бакет → DNS: blog.tatarinovms.ru / blog.tatarinovms.space → A → VDS → nginx reverse proxy → S3 (*.website.twcstorage.ru)
При пуше в main GitHub Actions сам собирает Hugo и синхронизирует с S3 — nginx просто проксирует то, что уже лежит в бакете. Редеплой занимает меньше минуты.
Timeweb S3 как хостинг статики — отличное решение. Но с кастомными доменами придётся извернуться. Надеюсь, когда-нибудь они добавят эту возможность, но пока — nginx.