Ghost CMS на VPS: self-hosted блог и newsletter без Substack
Ghost на VPS: Docker, MySQL, Nginx, SSL, темы, Members, Stripe, email newsletter и бэкапы. Полный гайд по независимому блогу и монетизации контента.
Краткий ответ: Ghost — Node.js CMS для блогов и email-рассылок. На VPS 2 GB+: Docker Compose + MySQL + Nginx + SSL + ежедневный backup. Members и Stripe — без комиссии платформы.
Substack берёт 10% с подписок, Medium контролирует аудиторию, WordPress перегружен плагинами. Ghost на вашем VPS — чистый редактор, встроенный newsletter, SEO из коробки и полный контроль над данными читателей.
Ghost vs WordPress vs Substack vs Hugo
| Критерий | Ghost | WordPress | Substack | Hugo (static) |
|---|---|---|---|---|
| Newsletter | Встроенный | Плагины | Встроенный | Нет |
| Members/paywall | Да | Плагины | Да | Нет |
| RAM на VPS | 1–2 GB | 512 MB–2 GB | N/A | 128 MB |
| Редактор | Block-based | Gutenberg/classic | Простой | Markdown files |
| Монетизация | Stripe 0% platform fee | WooCommerce | 10% fee | Своя логика |
| Self-hosted | Да | Да | Нет | Да |
Ghost — sweet spot между «просто блог» и «платформа подписок».
Архитектура production stack
Readers / Subscribers
↓ HTTPS
Nginx reverse proxy + Let's Encrypt
↓
Ghost container (Node.js, port 2368)
↓
MySQL 8 (content, members, settings)
↓
/var/lib/ghost/content (themes, images, files)
↓
SMTP relay (Postfix / Mailgun) — newsletter delivery
Опционально: Plausible для аналитики без Google, Authentik если Ghost в экосистеме SSO.
Требования к VPS
| Сценарий | RAM | CPU | Диск |
|---|---|---|---|
| Личный блог, <5k views/мес | 1 GB | 1 vCPU | 10 GB SSD |
| Активный блог + newsletter 1k subs | 2 GB | 2 vCPU | 20 GB SSD |
| Members + heavy media | 4 GB | 2 vCPU | 40 GB SSD |
Ghost на Node.js — умеренное потребление RAM. Bottleneck чаще MySQL и disk I/O при массовой рассылке.
Подготовка VPS
sudo apt update && sudo apt upgrade -y
sudo apt install -y docker.io docker-compose-plugin nginx certbot
sudo usermod -aG docker $USER
DNS: A запись blog.example.com → IP VPS. Для newsletter — SPF, DKIM, DMARC на домене (Postfix guide).
Docker Compose (production)
services:
ghost:
image: ghost:5-alpine
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
url: https://blog.example.com
database__client: mysql
database__connection__host: db
database__connection__user: ghost
database__connection__password: CHANGE_ME_STRONG
database__connection__database: ghost
mail__transport: SMTP
mail__options__host: smtp.example.com
mail__options__port: 587
mail__options__auth__user: ghost@example.com
mail__options__auth__pass: SMTP_PASSWORD
volumes:
- ./ghost-content:/var/lib/ghost/content
ports:
- "127.0.0.1:2368:2368"
db:
image: mysql:8.0
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: CHANGE_ME_ROOT
MYSQL_DATABASE: ghost
MYSQL_USER: ghost
MYSQL_PASSWORD: CHANGE_ME_STRONG
volumes:
- ./mysql-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
Первый запуск: docker compose up -d, затем https://blog.example.com/ghost для создания admin.
Nginx reverse proxy
server {
listen 443 ssl http2;
server_name blog.example.com;
ssl_certificate /etc/letsencrypt/live/blog.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.example.com/privkey.pem;
client_max_body_size 50M;
location / {
proxy_pass http://127.0.0.1:2368;
proxy_set_header Host $host;
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;
proxy_set_header X-Forwarded-Host $host;
}
}
SSL: Let’s Encrypt certbot. Альтернатива — Caddy с auto-HTTPS.
Первоначальная настройка Ghost
- Откройте
/ghost→ создайте owner account - Settings → General — title, timezone, locale (ru или en)
- Settings → Design — выберите тему (Casper default, или купите premium)
- Settings → Membership — включите members, tiers, Stripe
- Settings → Email newsletter — confirm SMTP, test email
- Settings → Integrations — Admin API key для CI/CD публикаций
Темы и кастомизация
| Источник | Цена | Примечание |
|---|---|---|
| Casper (built-in) | Free | Минималистичная, быстрая |
| Ghost Marketplace | $0–89 | Официальные темы |
| Custom theme | Dev time | Handlebars templates |
Темы живут в content/themes/. После изменений: Settings → Design → Restart.
Code injection (head/footer) — для Plausible script без правки темы.
Members, tiers и Stripe
Ghost Members — встроенная подписка без Substack:
- Settings → Membership → Stripe — подключите Stripe account
- Создайте Tiers (free / monthly / yearly)
- Posts → выберите visibility: Public / Members only / Paid only
- Portal URL:
/signup/— self-service управление подпиской
Комиссия: только Stripe (~2.9%), Ghost не берёт platform fee. Данные members в вашей MySQL.
Email newsletter best practices
| Практика | Зачем |
|---|---|
| Dedicated subdomain | news.example.com — reputation isolation |
| SPF + DKIM + DMARC | Inbox delivery, не spam |
| Double opt-in | GDPR, меньше жалоб |
| Unsubscribe one-click | Legal requirement |
| Batch size tuning | MySQL load при 10k+ subs |
Для больших рассылок — внешний ESP (Mailgun, SES) через SMTP. Ghost queue обрабатывает асинхронно.
SEO и performance
- Canonical URLs — Ghost генерирует автоматически
- Sitemap —
/sitemap.xml - Structured data — JSON-LD для articles
- Image optimization — загружайте WebP, lazy load в теме
- CDN — Cloudflare перед Nginx (cache static, не HTML admin)
Lighthouse: Ghost с Casper обычно 90+ performance out of box.
Интеграции и автоматизация
# Публикация через Admin API (CI)
curl -X POST "https://blog.example.com/ghost/api/admin/posts/" \
-H "Authorization: Ghost YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"posts":[{"title":"New post","status":"published"}]}'
Связки:
- Zapier/n8n — n8n self-hosted для webhook → Telegram
- GitHub Actions — draft из markdown в repo
- Analytics — Plausible
Backup и disaster recovery
| Что бэкапить | Как | Частота |
|---|---|---|
| MySQL dump | mysqldump ghost |
Daily |
| ghost-content volume | tar/rsync | Daily |
| Ghost config (env) | git secret repo | On change |
#!/bin/bash
# /opt/backup-ghost.sh
docker compose exec -T db mysqldump -u ghost -p$DB_PASS ghost > /backup/ghost-db.sql
tar czf /backup/ghost-content-$(date +%F).tar.gz ./ghost-content/
Стратегия 3-2-1: local + Restic offsite. Тест restore на staging VPS раз в квартал.
Security hardening
| Пункт | Реализация |
|---|---|
| HTTPS only | Nginx redirect 80→443 |
| Admin 2FA | Ghost native TOTP в staff settings |
| Rate limiting | Nginx limit_req на /ghost/api/ |
| Firewall | nftables — 80, 443 only |
| Updates | docker compose pull && up -d monthly |
| Admin via VPN | Tailscale restrict /ghost to mesh |
Не храните Stripe keys в git. Используйте .env с restricted permissions.
Мониторинг
- Uptime — Uptime Kuma ping
/api/health/(если plugin) или homepage - Logs —
docker compose logs -f ghost - MySQL — slow query log при тормозах admin
- Disk — alert при 80% (images + newsletter queue)
Миграция с WordPress / Medium / Substack
| Источник | Метод |
|---|---|
| WordPress | Ghost migration plugin (WP → JSON → Ghost) |
| Medium | Export ZIP → Ghost importer |
| Substack | Manual: posts copy + members CSV (limited) |
| Markdown repo | Custom script + Admin API |
Members email list — экспортируйте до миграции. Double opt-in может потребовать re-confirmation.
Troubleshooting
| Проблема | Решение |
|---|---|
| 502 Bad Gateway | Ghost container down, check docker compose ps |
| Images not loading | url в env не совпадает с реальным доменом |
| Email not sending | SMTP creds, port 587 TLS, check spam score |
| Slow admin | MySQL indexes, upgrade RAM, enable swap |
| Theme broken after update | ghost restart, check theme compatibility v5 |
| Stripe webhook fail | Verify endpoint URL HTTPS, check Stripe logs |
| Database connection | MySQL healthcheck, password mismatch in compose |
| Redirect loop | X-Forwarded-Proto в Nginx |
Связка с экосистемой Storm
- Reverse proxy — Nginx vs Caddy
- CI для контента — GitHub Actions
- Файлы и media offload — MinIO S3
- SSO для staff — Authentik
- Общий сервер — Docker Compose patterns
Итог
Ghost на VPS — лучший self-hosted выбор для автора, который хочет блог + newsletter + paid members без посредников. Setup за вечер, стоимость — только VPS.
VPS от 2 GB — StormNet Cloud. SSL — Let’s Encrypt. Backup — 3-2-1. Аналитика — Plausible.
Рекомендуем прочитать
Частые вопросы
Ghost или WordPress для блога на VPS?
Ghost — минимализм, встроенный newsletter, Node.js. WordPress — экосистема плагинов. Для авторского блога и рассылки — Ghost.
Сколько RAM нужно Ghost на VPS?
2 GB для малого блога, 4 GB при активном newsletter и Members. MySQL + Node.js одновременно.






