Build Ghost Pro
YESreplaces $29/mosaves $348/yrback to the verdict
Ghost, self-hosted properly: the open-source CMS in Docker with MySQL 8, Caddy for HTTPS, transactional email over SMTP, newsletters through Mailgun (the only bulk provider Ghost supports self-hosted), memberships wired to Stripe in test mode first, and a backup you have actually restored once. You save the subscription and take on upgrades and deliverability.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Every step below is a command you type or a file you edit.
Get it VS Code (code.visualstudio.com), Cursor or Zed. Open a folder for the project and use the editor's built-in terminal. open ↗
Verify
You can open a folder and run a command in its terminal - installfree
Why History for your code, and the way most hosts deploy.
Get it Install from git-scm.com or with your package manager, then run git init in the project folder once it exists. open ↗
Verify
git --version prints a version - accountabout $6 to $12 a month
Why Ghost plus MySQL need it. Ubuntu 24.04 with SSH.
Get it Hetzner CX22 or a DigitalOcean 2 GB droplet. open ↗
- installfree
Why Ghost and MySQL run as containers.
Get it Follow docs.docker.com/engine/install/ubuntu, then add your user to the docker group. open ↗
Verify
docker compose version prints a version - accountroughly $10 a year, or free on an existing domain
Why Ghost's url setting must be the exact public HTTPS address.
Get it Register at Cloudflare Registrar, Porkbun or Namecheap, or use a subdomain of one you already own. You add one DNS record in the deploy phase. open ↗
- API keyfree tiers exist
Why Password resets, staff invites and member sign-in links. Any SMTP provider.
Get it Postmark, Fastmail or Mailgun's SMTP: host, port, username, password.
- API keyfree trial, then usage-based
Why Newsletters will not send over SMTP. Ghost's bulk email is Mailgun-only for self-hosted installs as of 2026.
Get it Sign up at mailgun.com, add a sending domain (mail.yourdomain.com), add the DNS records it shows (SPF, DKIM, MX), then Settings > API Keys > create a key. Verify current provider support in Ghost's docs before relying on this. open ↗
- API keyfree; fees per transaction when live
Why Paid memberships. Test mode first, live as a separate deliberate step.
Get it dashboard.stripe.com > Developers > API keys (toggle Test mode). Ghost connects via Stripe Connect from its settings. open ↗
- accountcents a month
Why A backup on the server being backed up is not a backup.
Get it Backblaze B2 or Cloudflare R2 bucket plus rclone configured on the VPS. open ↗
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
GHOST_URL | required | https://yourdomain.com | The exact public address. Wrong here means broken links and mixed content. |
MYSQL_ROOT_PASSWORDsecret | required | long-random | openssl rand -base64 24. |
MYSQL_PASSWORDsecret | required | long-random | Ghost's database password. |
MAIL_TRANSPORT | required | SMTP | Ghost transactional mail transport. |
MAIL_HOST | required | smtp.postmarkapp.com | From your SMTP provider. |
MAIL_PORT | required | 587 | From your SMTP provider. |
MAIL_USERsecret | required | ... | SMTP username. |
MAIL_PASSsecret | required | ... | SMTP password. |
MAIL_FROM | required | 'Your Site' <hello@yourdomain.com> | From address for transactional mail. |
The build, in order
Bring it up locally
Ghost and MySQL running from one compose file, data surviving a restart.
Named volumes for content and the database, health checks, restart: unless-stopped, every secret from .env.
Files
docker-compose.yml.env- terminal
docker compose up -d docker compose logs -f ghost
done when · tick each as it passeswatch out- MySQL 8.0 or 8.4 only. Substituting MariaDB or SQLite is a migration you discover at the worst moment.
Domain and TLS
Public HTTPS, Ghost bound to localhost, url set correctly.
Your domain with reverse_proxy localhost:2368. Ghost's port published on 127.0.0.1 only.
Files
Caddyfile
done when · tick each as it passesTransactional email
Magic links and invites arrive in a real inbox.
mail__transport, mail__options__host, port, auth user and pass, mail__from.
Check the spam folder too; if they land there, fix SPF for the from domain before moving on.
done when · tick each as it passesNewsletter delivery
Bulk email through Mailgun with DNS that keeps it out of spam.
Mailgun shows the exact records. DMARC starts at p=none.
done when · tick each as it passeswatch out- Plain SMTP delivers your password resets and will not deliver your newsletter. These are two systems.
Memberships and payments
Tiers and gating working in test mode before a single live key exists.
done when · tick each as it passesTheme
Backup, and a restore drill
A nightly backup off the box and one restore you actually performed.
Files
backup.shterminaldocker compose exec -T mysql mysqldump -u root -p$MYSQL_ROOT_PASSWORD ghost > backups/ghost-$(date +%F).sql docker run --rm -v ghost_content:/c -v $PWD/backups:/b alpine tar czf /b/content-$(date +%F).tgz -C /c . rclone copy backups remote:ghost-backups/
Do this now, not later. Write the date in the README.
done when · tick each as it passesUpgrades and handover
A repeatable upgrade and the honest trade written down.
Files
update.shEvery DNS record and what breaks without it, the restore date, and the trade: you now own upgrades, patching, deliverability and support.
Files
README.md
done when · tick each as it passes
That is the whole plan for Ghost Pro. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- A custom theme from scratch and any custom blog engine code.
- Third-party analytics.
- A CDN and WAF in front; worth adding later.
- managed hosting
- upgrades
- backups
- email delivery setup
- CDN/security
- support
- easy scaling
- Cloudflare in front for caching and a WAF
- A staging stack from last night's backup to test upgrades on
Need the files? The project pack on the verdict page hands your agent the whole brief · more publishing.