Self-host Ghost® on a managed VPS: your publication, on your own server

To self-host Ghost on a managed VPS is to publish your articles, gather your members and send your newsletters from a machine of your own, which our teams keep up to date and monitor day and night. Ghost is free software under the MIT license. This guide follows its official documentation: Ghost-CLI or Docker Compose, MySQL, HTTPS, email, backups and updates.

Simon Vasconcelos Lee By Published on 9 November 202613-minute read

All guides

In short

  1. The MIT license lets you use Ghost freely in your company, as long as you keep its license notice. The name and logo fall separately under the Ghost Foundation trademark policy.
  2. The project recommends Ghost-CLI on Ubuntu, with Nginx, Node.js 22, MySQL 8 and at least 1 GB of memory. Its Docker Compose method, in “preview”, adds web analytics and self-hosted ActivityPub.
  3. MySQL 8 is the only database supported in production.
  4. Plan for two email paths: an SMTP relay for transactional email, which is essential, and Mailgun for newsletters, the only bulk email service supported.
  5. Back up the database and the content folder, then update with ghost update or docker compose pull: the project releases updates every week.

What Ghost does

Ghost is a professional, open source publishing platform built on Node.js. It brings together in one place what an independent publication often assembles from several tools.

According to the documentation, the product has the same features when self-hosted. What surrounds it is yours to arrange: newsletter delivery, web analytics, a CDN and full site backups.

The MIT license and the trademark

Ghost is released under the MIT license. It allows you to use, copy, modify, merge, publish, distribute, sublicense and sell the software, on one condition: keep the copyright notice and the license text in every copy. You can therefore run Ghost in your company, adapt it and publish for your clients, with no license to buy.

The license covers the code. The name and logo belong to Ghost Foundation and fall under its trademark policy: read it if you are building an offer around the software. Ghost is a registered trademark of Ghost Foundation.

This publication is independent of and not affiliated with, authorized, sponsored, or approved by Ghost Foundation.

Ghost-CLI or Docker Compose: two official methods

The project documents two paths for production. Ghost-CLI is the install it officially recommends, and Docker Compose is a “preview” that it describes as brand new. It also says it is moving toward this Docker tooling instead of updating Ghost-CLI, and it provides an assistant to go from one to the other.

ItemGhost-CLIDocker Compose (preview)
SystemUbuntu 22.04, 24.04 or 26.04A Linux server with Docker 20.10.13 or higher
MemoryAt least 1 GBThe project cites a server with 2 GB and 1 CPU as an example
Web server and HTTPSNginx and a Let’s Encrypt certificate, set up by Ghost-CLICaddy, in a container, which handles TLS
DatabaseMySQL 8.0 or 8.4 installed on the serverMySQL in a container
Web analyticsNot compatibleAvailable, with a Tinybird account
Social web (ActivityPub)The service hosted by the project, free within its usage limitsThe same service by default, or your own, self-hosted

Before you start: domain and server

Point the domain at the server before installing, because the certificate is issued during setup. On a Simafri VPS, visitors arrive on ports 80 and 443, under the server’s domain name, with A and AAAA records. The site address is written in full, with its protocol, such as https://example.com: an IP address causes errors.

Beyond the memory shown above, the project gives no figure for disk or load: the right size depends on your traffic, your images and your media. On a managed VPS, describe your publication to us and we will recommend the size that fits.

Install Ghost with Ghost-CLI on Ubuntu

Ghost-CLI runs under a user other than root, whose name is not ghost, since that name conflicts with the tool. Create it from the root account, replacing user with the name you choose:

Terminal, as root
adduser <user>
usermod -aG sudo <user>
su - <user>

Nginx and MySQL

Update the system, then install Nginx (1.9.5 at minimum for SSL) and MySQL. The ufw line opens HTTP and HTTPS if that firewall is active:

Terminal
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install nginx
sudo ufw allow 'Nginx Full'
sudo apt-get install mysql-server

On newer versions of Ubuntu, the MySQL root account authenticates through a socket, which Ghost does not support. Give it a password:

Terminal, then MySQL console
sudo mysql
ALTER USER 'root'@'localhost' IDENTIFIED WITH 'caching_sha2_password' BY '<your-new-root-password>';
FLUSH PRIVILEGES;
exit

Node.js and Ghost-CLI

Ghost requires Node.js 22: versions 20, 21, 23 and above are not supported. The project asks you to install it system-wide, this way:

Terminal
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
NODE_MAJOR=22
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
sudo apt-get update
sudo apt-get install nodejs -y
sudo npm install ghost-cli@latest -g

Run the install

Ghost installs in its own directory, with a specific owner and permissions. Replace sitename with the name of your choice:

Terminal
sudo mkdir -p /var/www/sitename
sudo chown <user>:<user> /var/www/sitename
sudo chmod 775 /var/www/sitename
cd /var/www/sitename
ghost install

The command then asks its questions, and the project recommends answering yes to each step it offers:

The site is now live, and https://example.com/ghost invites you to create the owner account. If the install is interrupted, ghost setup restarts the configuration; if it fails, ghost uninstall cleans up better than deleting the folder. ghost doctor checks the system at any time.

Install Ghost with Docker Compose (preview)

The project presents this method as a “preview”: to its eyes, it is brand new. It is the method that lets you self-host web analytics and ActivityPub.

On a server with Docker installed, fetch the official tooling into /opt/ghost and copy its two example files:

Terminal
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 the .env file, fill in these values:

In caddy/Caddyfile, uncomment the block for your case: redirecting www to the root domain, or the reverse, or a separate admin domain. Then start Ghost and finish the setup at /ghost:

Terminal
docker compose pull
docker compose up -d

Web analytics and ActivityPub

Analytics requires a Tinybird account, an external service. From /opt/ghost, log in, deploy its configuration and get the tokens to paste into .env, in the “Tinybird configuration” section:

Terminal
docker compose run --rm tinybird-login
docker compose run --rm tinybird-sync
docker compose run --rm tinybird-deploy
docker compose run --rm tinybird-login get-tokens

ActivityPub works right away with the service hosted by the project, free within usage limits published in its documentation. To self-host your own, uncomment ACTIVITYPUB_TARGET=activitypub:8080. Then enable the profile or profiles you want in .env, and restart the containers:

.env
COMPOSE_PROFILES=analytics,activitypub
Terminal
docker compose pull
docker compose up -d

For ActivityPub, the project also asks you to recreate Ghost and Caddy, with docker compose up -d --force-recreate ghost caddy.

Move from Ghost-CLI to Docker Compose

After the clone and the configuration, bash scripts/migrate.sh copies the content folder, imports the database into the MySQL container and starts Caddy in place of Nginx. It offers to stop Nginx, which takes the site offline: answer no if other sites run on the server. It deletes no data. bash recovery_instructions.sh restores the original site if you interrupt the migration.

HTTPS and the admin domain

The Ghost admin must run over HTTPS, and the project recommends HTTPS for the whole site: without it, usernames and passwords travel in plain text. With Docker Compose, Caddy handles it. With Ghost-CLI, the install sets up the certificate, and one command does it later:

Terminal, in the site folder
ghost setup ssl

To change the address of a site installed with Ghost-CLI, ghost config url https://example.com updates the configuration, and ghost restart applies it.

The project also advises a separate admin domain, which reduces the attack surface. The path remains /ghost/. With Ghost-CLI, you declare it in config.production.json:

config.production.json
"admin": {
  "url": "https://admin.example.com"
}

With Docker Compose, fill in ADMIN_DOMAIN, uncomment its block in the Caddyfile and run docker compose up -d --force-recreate ghost caddy. This domain also needs its own A and AAAA records.

MySQL 8, the production database

MySQL 8 is the only database the project supports in production. Ghost-CLI installs with MySQL 8.0 or 8.4, and the Compose file provides a MySQL 8.0 image.

Email and newsletters

Ghost distinguishes two kinds of sending, which do not take the same path. This is the most important thing to set up after the install.

SendingExamplesWhat carries it
TransactionalStaff invitations, password resets, member signups and login links, login code from a new deviceThe Ghost mail configuration, over SMTP
NewslettersArticles sent to your membersThe Mailgun API, whose keys you enter in the “Email newsletter” settings

The SMTP relay, for transactional email

The documentation lists this configuration as required in production. Ghost also emails a code when it detects a login from a new device: set up this relay before inviting your team.

On a Simafri VPS, sending goes through an authenticated SMTP relay, on port 587: the address of an email account you already have, or Simafri Suite.

config.production.json (Ghost-CLI), then ghost restart
"mail": {
  "transport": "SMTP",
  "from": "'Acme Support' <support@example.com>",
  "options": {
    "host": "smtp.example.com",
    "port": 587,
    "secure": false,
    "requireTLS": true,
    "auth": {
      "user": "support@example.com",
      "pass": "1234567890"
    }
  }
}
.env (Docker Compose), SMTP Email section
mail__transport=SMTP
mail__options__host=smtp.example.com
mail__options__port=587
mail__options__secure=false
mail__options__requireTLS=true
mail__options__auth__user=support@example.com
mail__options__auth__pass=1234567890
mail__from="'Acme Support' <support@example.com>"
Terminal, after changing the .env file
docker compose up -d --force-recreate ghost

Mailgun, for newsletters

The SMTP relay does not cover newsletters. According to the documentation, bulk email is not sent over SMTP, and Mailgun is, at present, the only bulk email service Ghost supports. You create your Mailgun account and enter its API keys in the Ghost settings.

If you do not send your articles by email, you need no Mailgun account at all: you can ignore those settings. If you prefer another newsletter tool, the project suggests syncing your members to it, for example with Zapier or its integration guides.

Back up and restore Ghost

Ghost offers three levels of copy, and they do not hold the same things. To rebuild a site exactly, the project asks you to back up the database and the content folder directly.

MethodWhat it holdsWhat it is for
ghost backup (Ghost-CLI)A zip: content as JSON, members as CSV, themes, images, files and media, routes and redirectsA precaution before an update
Export from the admin areaA zip: content and settings, members, post analytics, themes, routes and redirects, as you choose. No comments, member activity history or mediaMoving content to another site
Database and content folderAll of the dataDisaster recovery, or an exact copy

The export is under “Settings”, “Advanced”, “Import/Export”, “Export” tab, “All data” button. For the full copy with Ghost-CLI, the MySQL account and the database name to use are in config.production.json:

Terminal, Ghost-CLI
mkdir -p ~/ghost-backups
mysqldump -u ghost_user -p ghost_production > ~/ghost-backups/ghost_backup.sql
cd /var/www/sitename
tar czf ~/ghost-backups/ghost_content_backup.tar.gz content/

With Docker Compose, the official tooling keeps content in data/ghost. The dump follows the form given by the MySQL image, and the root password is already in the container. Also keep a copy of .env and the Caddyfile.

Terminal, Docker Compose
cd /opt/ghost
mkdir -p ~/ghost-backups
docker compose exec -T db sh -c 'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" ghost' > ~/ghost-backups/ghost_backup.sql
tar czf ~/ghost-backups/ghost_content_backup.tar.gz data/ghost

Restore

Terminal, Ghost-CLI
cd /var/www/sitename
ghost stop
mysql -u ghost_user -p ghost_production < ~/ghost-backups/ghost_backup.sql
tar xzf ~/ghost-backups/ghost_content_backup.tar.gz
sudo chown -R ghost:ghost content
ghost restart
Terminal, Docker Compose
cd /opt/ghost
docker compose stop ghost
docker compose exec -T db sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" ghost' < ~/ghost-backups/ghost_backup.sql
tar xzf ~/ghost-backups/ghost_content_backup.tar.gz
docker compose start ghost

Copy these files off the server, with rsync or scp, and run a test restore on a test machine. If you start again from an export with paid members, connect the same Stripe account before importing your members.

Update Ghost

The project releases updates every week and points out that a public web server must stay up to date. Read the release notes, then run the command for your method.

With Ghost-CLI

Terminal, in the site folder
ghost check-update
ghost backup
ghost update

With Docker Compose

Terminal
cd /opt/ghost
git pull
docker compose pull
docker compose up -d
docker image prune

The last line, which is optional, removes old images. Leave GHOST_VERSION commented out in .env: pinning a version stops these update commands from working.

Harden and grow your instance

The documentation gives several settings for a server in production:

For load, the project places a CDN and a caching layer in front of Ghost. It specifies that clustering and sharding are not supported: a single instance serves the site, and the cache absorbs the traffic.

Who does what on a managed VPS

Self-hosting means running the server as well as the application. On a managed VPS, our teams take charge of the server and proactively monitor your instance; Ghost remains your software, with its settings and its data.

TopicOur teamsYou
The serverSystem and security updates, server interventions includedThe time that suits your business
MonitoringServer watched 24/7, with intervention without waiting for your callYour content, your members and your subscriptions
GhostProactive monitoring: errors, slowdowns and disk space spotted, fix recommendedIts install, updates and settings, its SMTP relay and its Mailgun account, which you apply, yourself or with your AI assistant
BackupsEvery night, encrypted and kept off the machine; restores includedThe restore request, when you need one
MigrationYour current server migrated, complimentaryThe time of the switch

If you would rather have our teams step into the application itself, this service is billed by the hour or as a fixed fee, with an estimate shared before the work begins.

A server looked after for your Ghost site

With a Simafri managed VPS, our teams keep the server behind your Ghost instance up to date, watch it day and night and step in without waiting for your call; they also proactively monitor your instance and recommend useful fixes, which you apply, yourself or with your AI assistant. Backups are encrypted every night and kept off the machine, restores are included, and migration from your current server is complimentary.

Discover managed VPS

Frequently asked questions

Is Ghost free to self-host?

Yes. Ghost is under the MIT license: you can use it in your company and adapt it, as long as you keep its license notice. What remains is the services around it: the server, the email relay, Mailgun if you send newsletters, Tinybird if you want its analytics.

What are the requirements to self-host Ghost?

The project recommends Ubuntu 22.04, 24.04 or 26.04, Nginx, Node.js 22, MySQL 8.0 or 8.4, systemd, at least 1 GB of memory, a domain and a non-root user. For Docker Compose, it cites a server with 2 GB and 1 CPU as an example.

Should I install Ghost with Ghost-CLI or with Docker?

Ghost-CLI is the install the project recommends for production. Docker Compose is a preview, the only method that allows web analytics and self-hosted ActivityPub, and the project says it is moving toward this tooling. An assistant lets you move from Ghost-CLI to Docker Compose.

Does Ghost work with SQLite, MariaDB or PostgreSQL?

In production, MySQL 8 is the only supported database. The project recommends a reinstall on MySQL 8 from SQLite, and a migration to MySQL 8 from MariaDB. Its documentation makes no provision for PostgreSQL.

Can Ghost send newsletters over SMTP?

No. SMTP is for transactional email: invitations, passwords, member login links. Newsletters require the Mailgun API, the only bulk email service supported. If you do not send articles by email, you need no Mailgun account.

How do I back up a Ghost site on my own server?

For full recovery, back up the MySQL database with mysqldump and the content folder with tar. The ghost backup command produces a zip before an update. The admin export leaves out comments, member activity history and media.

How do I update Ghost?

With Ghost-CLI, run ghost backup then ghost update in the site folder, and ghost update --rollback if something goes wrong. With Docker Compose, run git pull, docker compose pull and docker compose up -d in /opt/ghost.

Does a managed VPS cover Ghost?

A managed VPS covers the server: system, security, monitoring and backups. Our teams also proactively monitor your instance and recommend useful fixes, which you apply. Work inside the application is billed by the hour or as a fixed fee.

Contact us