Self-host Ghost on a VPS

Run Ghost 6 with Ghost's own Docker setup (Ghost, MySQL 8 and Caddy) on your VPS: add your mail service, then start it on your domain with HTTPS.

To self-host Ghost, clone Ghost's own Docker setup (Ghost, MySQL 8 and Caddy), set your domain and your mail service's SMTP details in its .env file, and start it with Docker Compose; Caddy gets the HTTPS certificate. On VPSNine the one-click app prepares all of that in /opt/ghost, and one command, vps9-https blog.example.com you@example.com, starts Ghost on your domain with an owner account already made.

Ghost labels its Docker setup a preview. It's also the only way to run Ghost's self-hosted analytics and ActivityPub services.

What you need

  • A server. Budget (1 vCPU, 2 GB) runs a personal blog: Ghost's Docker guide uses a 1 CPU, 2 GB server as its example. Choose Standard for a busy site, or if you turn on Ghost's optional ActivityPub and analytics services, which add containers.
  • A domain such as blog.example.com.
  • An SMTP mail service, such as Mailgun, Postmark, Amazon SES or your own mail server. Ghost needs transactional email for logins, staff invites and password resets, so it won't run without it.

Install it with the one-click app

Order a server with the Ghost one-click app. On the first boot it installs Docker, clones Ghost's ghost-docker repository to /opt/ghost, generates the database passwords in /opt/ghost/.env, and pulls the images. Nothing runs yet: Ghost needs a domain and working mail before it can start.

  1. Put your mail service's SMTP details in /opt/ghost/.env, replacing the example values in the mail__ lines:
ini
mail__transport=SMTP
mail__options__host=smtp.example.com
mail__options__port=465
mail__options__secure=true
mail__options__auth__user=support@example.com
mail__options__auth__pass=1234567890
mail__from="'Example Blog' <support@example.com>"
  1. Point blog.example.com at the server with an A record for its IPv4 address, and allow ports 80 and 443 in the panel firewall.
  2. Run the start command with your domain and the email address for the owner account:
bash
vps9-https blog.example.com you@example.com
  1. Read your login, then sign in at https://blog.example.com/ghost/ and change the site title and your name in Settings:
bash
cat /root/ghost-admin.txt

Put it on your domain with HTTPS

Ghost's stack brings its own Caddy, so its vps9-https works differently from our other apps'. It refuses to run while .env still has the example SMTP host, then sets DOMAIN in /opt/ghost/.env and starts the stack with Docker Compose. Caddy listens on ports 80 and 443 and gets the Let's Encrypt certificate; Ghost and MySQL stay on Docker's internal network.

On a fresh install Ghost's setup page would make its first visitor the owner. vps9-https closes that gap: it creates the owner through Ghost's setup API from inside the server, with a generated password, before anyone else can reach the site, and writes the login to /root/ghost-admin.txt. Staff accounts are invite-only after that.

Doing it by hand on another server

Ghost's Docker install guide has the full steps. In outline, on a server with Docker (Install Docker):

bash
git clone https://github.com/TryGhost/ghost-docker.git /opt/ghost
cd /opt/ghost
cp .env.example .env
cp caddy/Caddyfile.example caddy/Caddyfile

In .env, set DOMAIN, set DATABASE_ROOT_PASSWORD and DATABASE_PASSWORD to random values (openssl rand -hex 32 makes one), and fill in the SMTP section. Then:

bash
docker compose pull
docker compose up -d

Open https://blog.example.com/ghost/ straight away and create the owner account, because whoever completes that page first owns the site. Ghost's other supported method, Ghost-CLI on Ubuntu with MySQL and nginx, is in the same docs.

Updates and backups

Ghost's update steps for its Docker setup are below; read the release notes first, and take a backup:

bash
cd /opt/ghost
git pull
docker compose pull
docker compose up -d

We turn on the operating system's automatic security updates; Ghost's images are yours to update.

Back up /opt/ghost/data (Ghost's content in data/ghost and the MySQL data in data/mysql) and /opt/ghost/.env off the server. Stop the stack for a consistent copy:

bash
cd /opt/ghost
docker compose stop
tar czf /root/ghost-$(date +%F).tgz data .env
docker compose start

Keep Ghost's own export (Settings, Import/Export) as well. Backups you should run shows how to copy archives off the server every night.

When it goes wrong

  • vps9-https says to put your SMTP details in /opt/ghost/.env first: the mail__options__host line still says smtp.example.com. Ghost emails you a code when you log in from a new device, so it can't log you in without mail.
  • You can't log in because the code email never arrives: the SMTP details are wrong. Fix them in .env, run docker compose up -d in /opt/ghost to recreate Ghost with them, and check docker compose logs ghost for mail errors.
  • Newsletters don't go out: the SMTP settings are only for transactional mail such as logins. Bulk newsletter sending goes through Mailgun, set up in Ghost's settings.
  • The browser shows a certificate warning: the domain doesn't point at the server yet, or port 80 or 443 is closed. Check getent hosts blog.example.com on the server and the panel firewall.

Be first when orders open

Join the waitlist and we'll send you one email when VPSNine launches. Pick a plan if you already know which one you want, and we'll size the first servers around it.

  • One email, on launch day
  • Monthly or yearly, no lock-in
  • 14-day money-back guarantee