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.
- Put your mail service's SMTP details in
/opt/ghost/.env, replacing the example values in themail__lines:
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>"- Point
blog.example.comat the server with anArecord for its IPv4 address, and allow ports 80 and 443 in the panel firewall. - Run the start command with your domain and the email address for the owner account:
vps9-https blog.example.com you@example.com- Read your login, then sign in at
https://blog.example.com/ghost/and change the site title and your name in Settings:
cat /root/ghost-admin.txtPut 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):
git clone https://github.com/TryGhost/ghost-docker.git /opt/ghost
cd /opt/ghost
cp .env.example .env
cp caddy/Caddyfile.example caddy/CaddyfileIn .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:
docker compose pull
docker compose up -dOpen 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:
cd /opt/ghost
git pull
docker compose pull
docker compose up -dWe 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:
cd /opt/ghost
docker compose stop
tar czf /root/ghost-$(date +%F).tgz data .env
docker compose startKeep 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-httpssays to put your SMTP details in/opt/ghost/.envfirst: themail__options__hostline still sayssmtp.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, rundocker compose up -din/opt/ghostto recreate Ghost with them, and checkdocker compose logs ghostfor 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.comon the server and the panel firewall.