Build Ghost Pro

YESreplaces $29/mosaves $348/yrback to the verdict

0%0 of 27 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

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.

estimated effort weekendthe files for this build are in the project pack

CMSThe official ghost Docker imageDatabaseMySQL 8.0 or 8.4 in DockerTLSCaddyNewslettersMailgunPaymentsStripe

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. 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

  2. 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

  3. about $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 ↗

  4. 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

  5. roughly $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 ↗

  6. 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.

  7. 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 ↗

  8. 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 ↗

  9. cents 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.

VariableNeededExampleWhere the value comes from
GHOST_URLrequiredhttps://yourdomain.comThe exact public address. Wrong here means broken links and mixed content.
MYSQL_ROOT_PASSWORDsecretrequiredlong-randomopenssl rand -base64 24.
MYSQL_PASSWORDsecretrequiredlong-randomGhost's database password.
MAIL_TRANSPORTrequiredSMTPGhost transactional mail transport.
MAIL_HOSTrequiredsmtp.postmarkapp.comFrom your SMTP provider.
MAIL_PORTrequired587From your SMTP provider.
MAIL_USERsecretrequired...SMTP username.
MAIL_PASSsecretrequired...SMTP password.
MAIL_FROMrequired'Your Site' <hello@yourdomain.com>From address for transactional mail.

The build, in order

  1. Bring it up locally

    Ghost and MySQL running from one compose file, data surviving a restart.

    1. Named volumes for content and the database, health checks, restart: unless-stopped, every secret from .env.

      Files docker-compose.yml.env

    2. terminal
      docker compose up -d
      docker compose logs -f ghost
    done when · tick each as it passes
    watch out
    • MySQL 8.0 or 8.4 only. Substituting MariaDB or SQLite is a migration you discover at the worst moment.
  2. Domain and TLS

    Public HTTPS, Ghost bound to localhost, url set correctly.

    1. 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 passes
  3. Transactional email

    Magic links and invites arrive in a real inbox.

    1. mail__transport, mail__options__host, port, auth user and pass, mail__from.

    2. Check the spam folder too; if they land there, fix SPF for the from domain before moving on.

    done when · tick each as it passes
  4. Newsletter delivery

    Bulk email through Mailgun with DNS that keeps it out of spam.

    1. Mailgun shows the exact records. DMARC starts at p=none.

    done when · tick each as it passes
    watch out
    • Plain SMTP delivers your password resets and will not deliver your newsletter. These are two systems.
  5. Memberships and payments

    Tiers and gating working in test mode before a single live key exists.

    done when · tick each as it passes
  6. Theme

    The default theme adjusted, not a theme from scratch.

    done when · tick each as it passes
  7. Backup, and a restore drill

    A nightly backup off the box and one restore you actually performed.

    1. Files backup.sh

      terminal
      docker 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/
    2. Do this now, not later. Write the date in the README.

    done when · tick each as it passes
  8. Upgrades and handover

    A repeatable upgrade and the honest trade written down.

    1. Files update.sh

    2. Every 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
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more publishing.