How to Self-Host Ghost on a VPS
Install Ghost on Ubuntu 24.04 with Ghost-CLI, Nginx, MySQL and Let's Encrypt, then set up SMTP mail, backups and updates on a VeloxaHost VPS.
Ghost is a Node.js publishing platform for blogs and newsletters, with member sign-ups and paid subscriptions built in. This guide installs it by hand on Ubuntu 24.04 with Ghost-CLI, Ghost's official production method: Nginx in front, MySQL, a Let's Encrypt certificate and a systemd service. Ghost's Docker Compose install is still labeled a preview, so it is not used here.
Prefer one click? Deploy Ghost from the VeloxaHost marketplace and skip the manual steps; this guide is for when you want to run every command yourself.
What you need
- A server running Ubuntu 24.04 LTS with root access. Ghost supports Ubuntu 22.04, 24.04 and 26.04, and asks for at least 1 GB of memory.
- A registered domain. Ghost's docs warn that an IP address as the site URL causes errors.
- A dedicated IPv4 address for the server, so Nginx can answer on ports 80 and 443 (more on this below).
- An SMTP account with a mail provider for transactional email such as login links and staff invites.
On VeloxaHost, pick Ubuntu 24.04 LTS as the image when you create the server. A Veloxa 1 (1 vCPU, 2 GB RAM, 50 GB disk) at $8 a month, billed at $0.02 an hour and capped at the monthly price, fits a typical blog. You can work over SSH or from the browser terminal on the server's page in the console.
Install Ghost
These steps follow Ghost's Ubuntu guide, which runs Ghost-CLI as a normal sudo user rather than root.
Give the server a dedicated IPv4 address. A VeloxaHost server gets a block of ports on a shared IPv4 address, and ports 80 and 443 are not available there. A dedicated IPv4 address is a paid add-on (the console shows the current price): choose it as "Network access" when you create the server, or take one later from the IP addresses page. If none is free in the region, the request is refused. The server must answer on the address, so add it to the interface (check the name with
ip addr) and to the network configuration so it survives a reboot.ip addr add 198.51.100.5/32 dev eth0Then point your domain's A record at the address. Ghost-CLI configures TLS during setup, so DNS must resolve before you run the installer. IPv6 is not available in US East yet, so skip the AAAA record.
Log in as root and create a user. Do not call it
ghost; that name conflicts with Ghost-CLI.ssh -p 20000 root@203.0.113.10 # the first port of your block forwards to SSH adduser deploy usermod -aG sudo deploy su - deployUpdate the system packages.
sudo apt-get update sudo apt-get upgradeInstall Nginx.
sudo apt-get install nginxInstall MySQL and give the MySQL root user a password. Ubuntu sets root up for socket authentication, which Ghost does not support.
sudo apt-get install mysql-server sudo mysqlALTER USER 'root'@'localhost' IDENTIFIED WITH 'caching_sha2_password' BY '<your-new-root-password>'; FLUSH PRIVILEGES; exitInstall Node.js 22, the only version Ghost supports, from NodeSource.
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 -yInstall Ghost-CLI.
sudo npm install ghost-cli@latest -gCreate the site directory, owned by your user, and run the installer.
sudo mkdir -p /var/www/sitename sudo chown deploy:deploy /var/www/sitename sudo chmod 775 /var/www/sitename cd /var/www/sitename ghost installFor the blog URL, enter your full HTTPS address, such as
https://example.com. MySQL hostname islocalhost; userootand the password from step 5, pick a database name, and let Ghost-CLI create its own MySQL user. Say yes to Nginx, SSL (it asks for an email for certificate notices), systemd and starting Ghost.
Ghost itself listens on 127.0.0.1:2368, and Nginx proxies your domain to it.
First login
Open https://example.com/ghost. Ghost prompts you to create the owner account for the site. Use an email address you can read, because Ghost sends login codes there.
Next, set up mail. Ghost marks mail as required in production: it sends staff invitations, password resets, member sign-ups and member login links. VeloxaHost blocks outbound TCP port 25, so use your provider's port 465 or 587. Edit config.production.json in the site directory:
"mail": {
"from": "'Example Blog' <support@example.com>",
"transport": "SMTP",
"options": {
"host": "smtp.example.com",
"port": 465,
"secure": true,
"auth": {
"user": "YOUR-SMTP-USER",
"pass": "YOUR-SMTP-PASSWORD"
}
}
},
For port 587, set "port": 587, "secure": false and "requireTLS": true so the connection upgrades with STARTTLS. Leave out the service preset when you set a port: Ghost's docs warn that presets override your own port. Then restart and send yourself a staff invite to test.
ghost restart
Secure it
Start with a VeloxaHost firewall. It is network-level and stateful, and once attached it drops inbound traffic no rule allows. In the console go to Firewalls, then New Firewall, then Add Rule, and create:
- allow TCP 22 from
YOUR.IP/32only - allow TCP 80 and 443 from
0.0.0.0/0
Attach it on the server's page. MySQL and Ghost's port 2368 stay closed to the outside.
Then follow Ghost's own server hardening list:
- Keep the site on HTTPS. Ghost Admin must run over HTTPS.
- Run
sudo mysql_secure_installation. Ghost strongly recommends it after setup. - Disable SSH password logins and use SSH keys only.
- Consider a separate admin domain (
admin.urlinconfig.production.json) to reduce attack vectors. - Leave staff device verification on: Ghost emails a code for logins from a new device. To require codes on every staff login, use Settings, then Staff, in Ghost Admin.
Back it up
Ghost keeps posts, members and settings in MySQL, and images, themes and media in the content folder. Ghost-CLI can bundle the important parts into one zip file:
cd /var/www/sitename
ghost backup
For disaster recovery, Ghost's docs recommend dumping the database and archiving the content folder directly. The database user and name are in config.production.json.
mkdir -p ~/ghost-backups
mysqldump -u <db-user> -p <db-name> > ~/ghost-backups/ghost_backup.sql
cd /var/www/sitename
tar czf ~/ghost-backups/ghost_content_backup.tar.gz content/
Copy the files off the server, for example with rsync -av -e "ssh -p 20000" root@203.0.113.10:/home/deploy/ghost-backups/ ./ghost-backups/.
On VeloxaHost, add a schedule too. Automatic snapshots on the server page run every hour, 6 hours, 12 hours, daily or weekly, kept 1, 3, 7, 14 or 30 days; restoring one overwrites the server's current state. The Backups page schedules full server backups every 6 hours, 12 hours, daily or weekly, keeping 1 to 10, and lets you download one for an off-site copy.
Update it
Take a snapshot in the console first. Then, from the site directory as your install user:
cd /var/www/sitename
ghost check-update
ghost backup
ghost update
Keep Ubuntu patched too with sudo apt-get update && sudo apt-get upgrade. If you ever change the Node.js major version, Ghost's docs say to upgrade Node first, then run ghost update <current-version> --force to rebuild dependencies, and not to update Node and Ghost at the same time.
Troubleshooting
The site does not start or shows an error page. Run ghost log -e for the error log and ghost doctor to check the environment.
SSL setup failed during install. The domain must resolve to the dedicated IPv4 and ports 80 and 443 must be open in your firewall. Fix DNS, then run ghost setup ssl.
Email fails with "Connection timeout". The SMTP port is blocked or wrong. Use 465 or 587, not 25, and remove any service preset that forces a different port. Run ghost restart after each change.
The install was interrupted. Run ghost setup to resume. If it went badly wrong, ghost uninstall removes it cleanly so you can start again.
FAQ
Which VeloxaHost size fits Ghost?
A Veloxa 1 (1 vCPU, 2 GB RAM, 50 GB disk) at $8 a month, billed at $0.02 an hour and capped at the monthly price, suits most blogs. Ghost's minimum is 1 GB of memory, but MySQL and Node.js share the server, so 2 GB gives more room.
What license is Ghost under?
Ghost is released under the MIT license. You can use, modify and host it without license fees; Ghost(Pro) is the separate paid hosted service.
Should I use Ghost's Docker install instead?
Ghost calls its Docker Compose tooling, with Caddy and MySQL, a preview; Ghost-CLI on Ubuntu is its officially supported stack. Try Docker if you need to self-host web analytics or the ActivityPub service.
Do social web features work on a Ghost-CLI install?
Yes. Ghost-CLI installs use the free hosted ActivityPub service from Ghost(Pro), up to 2000 followers, 2000 following and 100 interactions a day. Web analytics is not compatible with Ghost-CLI installs; it needs the Docker Compose method.
Can I send newsletters from a self-hosted Ghost?
Yes, through Mailgun, which Ghost requires for bulk email. Login links and invites use the SMTP settings in config.production.json.
Can I run Ghost without a dedicated IPv4 address?
Not with this method. Ghost-CLI's Nginx and Let's Encrypt setup expects your domain on ports 80 and 443, which the shared address does not offer. Ghost also needs a real domain as its URL, so the dedicated IPv4 add-on is the practical route for a public site.
Commands and settings checked on 2026-10-18 against the official documentation: docs.ghost.org, github.com. Software changes; if a step no longer matches, the vendor's docs win.
Ready to deploy?
Start a free trial, or compare plans to match your team's scale.