How to Install Ghost CMS on Ubuntu 24.04 — Self-Hosted Publishing with Memberships
Ghost is the modern publishing platform that powers sites for Stripe, DuckDuckGo, Mozilla, Kickstarter, and thousands of independent writers running paid newsletters. It combines a clean Markdown-first editor, a fast Node.js runtime, built-in paid membership and subscription billing through Stripe, and native bulk email — all in one open-source package you can host on your own VPS. This guide walks you through installing Ghost on a fresh Ubuntu 24.04 server using the official Ghost-CLI, from the first apt update through a production-hardened deployment with Stripe-powered subscriptions and Mailgun-powered newsletters.
Skip the setup? Deploy Ghost in under two minutes on a production-tuned VPS. Launch a Starter VPS now and start publishing today.
Table of Contents
What is Ghost?
Ghost is an open-source Node.js publishing platform created in 2013 as a Kickstarter-funded alternative to bloated, plugin-heavy CMS platforms. It focuses on a single use case — professional writing and newsletters — and does it exceptionally well. Ghost ships with a distraction-free Markdown editor, built-in SEO metadata and structured data, AMP pages, a headless Content API, a rich Admin API, webhooks, a full members database, Stripe subscription billing, bulk email newsletter sending, and a first-class theme system. Everything a modern paid newsletter or multi-author publication needs is in the core product — no plugins, no marketplace, no sprawling configuration.
Under the hood, Ghost is a Node.js application that stores content in MySQL 8, serves requests through Express, renders Handlebars themes, and persists images and files to local disk or an S3-compatible store. The project is maintained by the non-profit Ghost Foundation (registered in Singapore) and is released under the MIT license. The codebase lives at github.com/TryGhost/Ghost and the exceptionally thorough official documentation sits at ghost.org/docs/. The canonical self-host reference is ghost.org/docs/install/ubuntu/ — keep that tab open alongside this guide.
The standout feature is the Members layer. Ghost bundles a full subscription platform: anonymous visitors can become free signups, free signups can upgrade to paid monthly or yearly tiers via Stripe Checkout, Ghost tracks each member's status, and every post can be gated as public, members-only, or tier-only. When you publish a post, Ghost automatically emails it out to subscribers of the relevant tier. Running that stack anywhere else would normally require WordPress plus MemberPress plus Mailchimp plus Stripe integration plus a caching plugin — Ghost bakes all of it into a single Node process.
Why Self-Host Ghost Instead of Ghost(Pro), Substack, or Medium?
The honest comparison with competing platforms comes down to four numbers: price, revenue share, data ownership, and customization ceiling.
Ghost(Pro) is the Ghost Foundation's managed SaaS. It is the simplest on-ramp and directly funds the open-source project, which matters. But the pricing is member-count-based: Starter is USD 31/month for up to 500 members, Creator is USD 56/month for up to 1,000 members, and Team is USD 116/month for up to 2,000 members. Cross any member threshold and you jump to the next plan. A self-hosted VPS does not care how many members you have — a 2 GB server happily serves 10,000 members at a flat monthly price.
Substack charges nothing up front but takes 10% of every paid subscription, plus Stripe's processing fee. On USD 50,000 of annual newsletter revenue, that is USD 5,000 paid to Substack every year, indefinitely. Substack also owns the customer relationship, forbids certain content categories under opaque policies, and you cannot move your domain or theme independently.
Medium pays writers from a central pool based on member read time and charges readers a flat USD 5/month to read everything on the network. You do not own your subscribers, you cannot move your domain, you cannot control your pricing, and you cannot build a business around a single topic — Medium's algorithm decides what gets recommended.
Cost Comparison: Self-Hosted Ghost vs. SaaS Alternatives
| Scenario | Ghost on CloudCore Starter | Ghost(Pro) Creator | Substack | Medium Partner |
|---|---|---|---|---|
| Base monthly cost | EUR 7.99 | USD 56 (~EUR 52) | USD 0 | USD 0 |
| Per-member fees | None | Jumps at 1,000 / 2,000 / 10,000 | None | None |
| Revenue share | 0% (you keep all) | 0% (you keep all) | 10% | N/A (read-time pool) |
| Member count at first price break | Unlimited | 1,000 | Unlimited | Unlimited |
| Custom domain | Yes | Yes | Yes | Paid plan only |
| Custom theme | Yes (full Handlebars) | Yes (full Handlebars) | No (fixed) | No (fixed) |
| Own your email list | Yes | Yes | Yes (exportable) | No |
| Own the database | Yes | No | No | No |
| Annual cost at USD 50k revenue | EUR 107 | USD 672 | USD 5,000 | Pool-dependent |
CloudCore Pricing Tiers for Ghost
| Tier | Specs | Ideal For | Monthly |
|---|---|---|---|
| Starter | 2 vCPU, 2 GB RAM, 40 GB NVMe | New Ghost site, up to ~3,000 members | EUR 7.99 |
| Professional | 4 vCPU, 8 GB RAM, 100 GB NVMe | Active publication, 10,000+ members, heavy newsletter sends | EUR 19.99 |
| Business | 8 vCPU, 16 GB RAM, 200 GB NVMe | Multi-site Ghost, 50,000+ members, headless frontend | EUR 39.99 |
- Full server access to add custom integrations, run cron jobs, connect to internal APIs, or install sidecar services like Plausible Analytics.
- Full theme control — edit Handlebars templates, inject custom helpers, version-control themes in Git.
- Compliance control for GDPR, SOC 2, or data-residency mandates — your database lives where you choose.
- No platform risk — a terms-of-service change cannot demonetize or deplatform you.
Prerequisites
Before you begin, make sure you have:
- A VPS running Ubuntu 24.04 LTS with root or sudo access. The Ghost-CLI installer is sensitive to the distribution — use Ubuntu 22.04 or 24.04, not Debian or RHEL derivatives.
- A domain name (for example
blog.example.com) with an A record pointing to your VPS's public IPv4. Ghost-CLI requests a Let's Encrypt certificate during install and the domain must resolve correctly first. Verify withdig +short blog.example.com. - SSH access to your server as root or a sudo user.
- At least 1 GB of RAM for a small blog with a few hundred members. 2 GB+ strongly recommended once newsletters and the member portal are active.
- At least 10 GB of free disk for Ghost, Node, MySQL, images, and backups.
- A Stripe account if you plan to enable paid subscriptions (free to create, pay-as-you-go fees only).
- A Mailgun account for newsletter delivery (free sandbox tier for testing; production sending requires a paid plan and a verified domain).
Recommended Plan: CloudCore Starter>
For a new Ghost site with up to a few thousand members, we recommend the CloudCore Starter VPS:>
- 2 vCPU cores
- 2 GB RAM
- 40 GB NVMe SSD
- Unmetered bandwidth
- EUR 7.99/month>
This gives Ghost, MySQL, and Nginx headroom to serve a small-to-medium publication comfortably. Expect to step up to Professional (8 GB RAM, EUR 19.99/month) once you cross 10,000 members or run heavy weekly newsletter sends.
Connect to your server:
ssh root@your-server-ipStep 1: Update System Packages
Always start with an up-to-date package index. Ghost-CLI inspects kernel and package versions during install and complains about stale systems.
sudo apt update && sudo apt upgrade -yIf a kernel upgrade occurred, reboot before continuing:
sudo rebootReconnect via SSH after the reboot completes.
Step 2: Create a Non-Root Ghost User
Ghost-CLI refuses to run as root for security reasons. The convention is a dedicated non-root sudo user — the Ghost docs suggest the name ghost-mgr to avoid conflicting with the ghost system user that the installer will create later.
sudo adduser ghost-mgr
sudo usermod -aG sudo ghost-mgrSet a password when prompted and leave the rest blank. Switch to the new user:
su - ghost-mgrFrom this point on, run every command as ghost-mgr with sudo when required.
Why not just name the userghost? Ghost-CLI creates its own system user namedghostduringghost installto own the Node process. Using the same name for the admin account confuses the installer and produces cryptic permission errors — keep them separate.
Step 3: Install Node.js 20 LTS
Ghost 5.x supports Node.js 18 LTS and 20 LTS. Newer Node releases (21, 22) are unsupported and ghost install will refuse to run. The Ubuntu 24.04 default repos ship a Node version Ghost does not support — install Node 20 from the official NodeSource repository:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejsVerify the install:
node --versionv20.x.x
npm --version
10.x.x
If you already have a newer Node installed, remove it first: sudo apt remove -y nodejs && sudo apt autoremove -y, then rerun the commands above.
Step 4: Install and Secure MySQL 8
Ghost 5.x requires MySQL 8 in production. MariaDB is a popular drop-in replacement elsewhere, but Ghost-CLI's ghost doctor explicitly checks for MySQL and flags MariaDB as unsupported — keep it simple and install MySQL 8.
sudo apt install -y mysql-serverSet a strong password for the MySQL root user and switch it to mysql_native_password (MySQL 8 defaults to caching_sha2_password, which Ghost-CLI's probe sometimes rejects):
sudo mysqlIn the MySQL prompt:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'STRONG_ROOT_PASSWORD';
FLUSH PRIVILEGES;
EXIT;Run the security hardening script and answer "Y" to every prompt:
sudo mysql_secure_installationCreate the Ghost database and user:
sudo mysql -u root -pCREATE DATABASE ghost_production CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'ghostuser'@'localhost' IDENTIFIED WITH mysql_native_password BY 'STRONG_GHOST_PASSWORD';
GRANT ALL PRIVILEGES ON ghost_production.* TO 'ghostuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;A couple of notes:
utf8mb4is the only character set you should use. The older MySQLutf8is a three-byte truncation that corrupts emoji and CJK.localhostscoping means the credentials cannot be abused from the internet even if they leak.
ghost install if you provide the MySQL root password — pre-creating it is easier to audit and easier to rerun if the install fails halfway through.Step 5: Install Nginx
Ghost-CLI uses Nginx as the TLS-terminating reverse proxy in front of the Node.js process. Install it from the Ubuntu repositories:
sudo apt install -y nginxAllow HTTP and HTTPS through the firewall:
sudo ufw allow 'Nginx Full'
sudo ufw allow OpenSSH
sudo ufw enableConfirm Nginx is running:
sudo systemctl status nginxVisiting http://your-server-ip should now return the default Nginx welcome page. Ghost-CLI will replace this configuration with one specific to your Ghost site during install.
Step 6: Install Ghost-CLI
Ghost-CLI is the official command-line tool that drives every Ghost install, update, and configuration task. It is distributed via npm:
sudo npm install -g ghost-cli@latestVerify the install:
ghost --version
1.25.x or higher
Ghost-CLI is the only tool you should ever use to manage a Ghost install. Editing files by hand or running node index.js directly will cause ghost update to fail later.
Step 7: Run ghost install
Create a clean install directory owned by ghost-mgr. The convention is /var/www/ghost:
sudo mkdir -p /var/www/ghost
sudo chown ghost-mgr:ghost-mgr /var/www/ghost
sudo chmod 775 /var/www/ghost
cd /var/www/ghostOwnership matters — ghost-mgr needs write access, the later-created ghost system user needs group read/execute. 775 satisfies both. Do not cd in as root and run the install — that bakes root-owned files into the tree that Ghost-CLI cannot clean up later.
Run the installer from inside this directory:
ghost installGhost-CLI launches an interactive wizard. Typical answers:
? Enter your blog URL: https://blog.example.com
? Enter your MySQL hostname: localhost
? Enter your MySQL username: ghostuser
? Enter your MySQL password: STRONG_GHOST_PASSWORD
? Enter your Ghost database name: ghost_production
? Do you wish to set up "ghost" MySQL user? No
? Do you wish to set up Nginx? Yes
? Do you wish to set up SSL? Yes
? Enter your email (for SSL registration): [email protected]
? Do you wish to set up Systemd? Yes
? Do you want to start Ghost? YesBehind the scenes, Ghost-CLI:
/var/www/ghost/versions/X.Y.Z/ghost_production/var/www/ghost/config.production.json with your URL, DB credentials, and mail defaults/etc/nginx/sites-available/blog.example.com.conf/lib/systemd/system/ghost_blog-example-com.serviceExpected final output:
✔ Configuring Ghost ✔ Setting up instance ✔ Running database migrations ✔ Configuring Nginx ✔ Configuring SSL ✔ Configuring systemd ✔ Starting GhostGhost was installed successfully! To complete setup of your publication, visit:
https://blog.example.com/ghost/
Visit the admin URL in a browser. Create your owner account by entering your name, email, and password. You now have a working Ghost install on your own VPS.
The generated Nginx config reverse-proxies to Ghost on 127.0.0.1:2368 — the Node process never touches the public internet directly. client_max_body_size 1g covers large media uploads; raise it higher if you hit 413 Request Entity Too Large on a large video embed.
SSL Auto-Renewal
Ghost-CLI installs an acme.sh cron job that renews certificates automatically every 60 days. Confirm with:
sudo crontab -l | grep acmesudo certbot renew does not apply here — Ghost-CLI uses acme.sh, not Certbot.
Step 8: Configure Mailgun for Newsletters
Ghost separates mail into two channels: transactional (password resets, member signup confirmations) and bulk (newsletters sent to all subscribers). Transactional email can use any SMTP provider. Bulk email must use Mailgun, which is the only officially supported bulk provider — Ghost has a native Mailgun integration that handles list segmentation, unsubscribe tracking, and delivery analytics.
Sign up at mailgun.com, verify your sending domain (typically a subdomain like mg.example.com), and publish the SPF, DKIM, and DMARC DNS records Mailgun prints. Then collect two values from the Mailgun dashboard:
- Private API key (starts with
key-...) - SMTP password for
[email protected]
Transactional SMTP
Open the Ghost config file:
cd /var/www/ghost
nano config.production.jsonAdd a mail block for transactional SMTP (Mailgun's SMTP endpoint works well here):
"mail": {
"transport": "SMTP",
"from": "'Your Publication' <[email protected]>",
"options": {
"service": "Mailgun",
"host": "smtp.mailgun.org",
"port": 465,
"secure": true,
"auth": {
"user": "[email protected]",
"pass": "YOUR_MAILGUN_SMTP_PASSWORD"
}
}
}Restart Ghost to apply the change:
ghost restartBulk Newsletter API
In Ghost Admin → Settings → Email newsletter → Mailgun config, paste your Mailgun region (US or EU), sending domain, and private API key. Ghost Admin stores these in the database — do not put them in config.production.json.
Send a test email from Settings → Labs → Send test email to confirm transactional mail works, then publish a test post with the "Email to subscribers" option to confirm bulk delivery works. Never try to send newsletters directly from your VPS's Postfix — you will land in Gmail's spam folder within the first batch.
Step 9: Enable Stripe Memberships
Paid subscriptions are Ghost's flagship feature. The integration uses Stripe Checkout and Stripe Customer Portal — no custom billing code, no membership plugins.
In the Stripe dashboard, switch to live mode (after testing in test mode) and grab your secret key from Developers → API keys. Do not use a restricted key — Ghost needs the full Secret key so it can create products, prices, and checkout sessions programmatically.
In Ghost Admin:
https://blog.example.com/members/webhooks/stripeNext, configure tiers in Settings → Tiers:
- The Free tier is built in — members can sign up without paying to receive free newsletter content
- Click Add tier to create Monthly and Yearly paid tiers with your chosen prices (for example EUR 8/month, EUR 80/year)
- Each tier gets a benefits list, a welcome page, and an optional welcome email
- Signup options — which fields to show, which tiers to offer on the signup modal
- Account page — what members see when they click "Account" in the Portal
- Signup terms — your terms-of-service link
For a partial-content paywall with a public teaser, drop a <!--members-only--> block in the editor — content above the block is public, content below is gated.
Step 10: Install and Customize the Casper Theme
Ghost ships with Casper, the official default theme, which is production-ready and the benchmark every third-party theme measures against. It is fully responsive, accessibility-audited, and supports every Ghost feature (members, tiers, galleries, cards, newsletters, tags, authors). For a new site, stick with Casper until you have a reason not to.
To customize Casper, clone the repo, edit the Handlebars templates and SCSS, build a zip, and upload it:
git clone https://github.com/TryGhost/Casper.git my-theme
cd my-theme
npm install
npm run zipThen upload the generated zip via Ghost Admin → Design → Change theme → Upload theme. Ghost validates the theme with gscan and either activates it or lists exactly which rules it violates.
For a commercial or more distinctive look, browse the Ghost Marketplace — both free and paid themes. Popular picks include Source (Ghost's newer default), Lyra, Edition, and Digest. Any theme that passes gscan runs on self-hosted Ghost identically to Ghost(Pro).
Code Injection Without Editing the Theme
Settings → Code injection provides a global <head> and <body> textbox. Drop an analytics snippet, a font preload, or a Clarity tag there instead of touching the theme. Per-post injection is available under each post's settings panel.
Step 11: systemd Management, Updates, and Backups
Ghost runs as a systemd unit named after your install directory. For /var/www/ghost pointing at blog.example.com, the service name is typically ghost_blog-example-com.service.
Standard Management Commands
# From inside /var/www/ghost — preferred, uses Ghost-CLI wrapper:
ghost status
ghost restart
ghost stop
ghost start
ghost logOr via systemd directly:
sudo systemctl status ghost_blog-example-com
sudo journalctl -u ghost_blog-example-com -fUpdating Ghost
To upgrade to the latest Ghost version in the same major release:
cd /var/www/ghost
ghost updateGhost-CLI downloads the new version into versions/, stops the current one, runs database migrations, starts the new version, and rolls back automatically if anything fails. For major version jumps (Ghost 5 → 6), pass --major and read the upgrade notes at ghost.org/docs/faq/upgrading first.
Before any update, run ghost doctor to catch environment problems — it checks file permissions, Node version, MySQL connectivity, disk space, the systemd unit, and Nginx config sanity.
Backups
Ghost data lives in three places:
ghost_production)content/ directory (images, theme uploads, routes.yaml, redirects)config.production.json (credentials)Back up all three nightly. Create /usr/local/bin/ghost-backup.sh:
#!/bin/bash set -euo pipefail DATE=$(date +%F_%H%M) BACKUP_DIR=/var/backups/ghost mkdir -p "$BACKUP_DIR"mysqldump --single-transaction --quick \ -u ghostuser -p'STRONG_GHOST_PASSWORD' ghost_production \ | gzip > "$BACKUP_DIR/db_$DATE.sql.gz"
tar -czf "$BACKUP_DIR/content_$DATE.tar.gz" \ -C /var/www/ghost content config.production.json
find "$BACKUP_DIR" -type f -mtime +14 -delete
Make it executable and schedule it:
sudo chmod 700 /usr/local/bin/ghost-backup.sh
sudo crontab -e
Add:
0 3 * /usr/local/bin/ghost-backup.sh >> /var/log/ghost-backup.log 2>&1Mirror /var/backups/ghost offsite with restic or rclone for disaster recovery. Test the restore path once a quarter — a backup you have never restored from is a hope, not a backup.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
Error: Your Node version is not supported | Installed Node is not an even-numbered LTS | Uninstall current Node, reinstall Node 20 LTS from NodeSource, then retry ghost install. |
Error: Unable to connect to MySQL during install | Wrong MySQL password or MySQL not listening | Verify with mysql -u ghostuser -p. Confirm MySQL is running: sudo systemctl status mysql. |
MySQL auth failure: caching_sha2_password | Default MySQL 8 auth plugin not supported by Ghost-CLI probe | ALTER USER 'ghostuser'@'localhost' IDENTIFIED WITH mysql_native_password BY '…'; |
| Let's Encrypt certificate request fails | DNS not pointing at server, or port 80 blocked | Confirm dig +short blog.example.com returns your VPS IP. Open port 80: sudo ufw allow 80. Retry ghost setup ssl. |
ghost install refuses to run as root | Security check | Create a non-root sudo user (Step 2) and run the installer as that user. |
| 502 Bad Gateway from Nginx | Ghost Node process crashed | ghost log --error. Common causes: MySQL down, OOM kill, config syntax error. Restart: ghost restart. |
| Newsletter sends to 0 subscribers | Mailgun not configured or domain unverified | Re-enter key in Settings → Email newsletter → Mailgun config. Verify DNS records in Mailgun dashboard. |
| Stripe Checkout returns 500 error | Wrong Stripe key mode (test key in live mode or vice versa) | Disconnect Stripe in Ghost Admin and reconnect with the correct mode's secret key. |
ghost update fails with migration error | Database permissions or corrupted migration | Run ghost doctor. Restore from your latest backup, then rerun the update. |
| Image upload returns 413 | Nginx client_max_body_size too low | Already set to 1g by Ghost-CLI. If you lowered it, raise it back and sudo systemctl reload nginx. |
Permission denied on content/ | Ran a command with sudo that shouldn't have been | sudo chown -R ghost:ghost /var/www/ghost/content, then ghost doctor. |
| High memory, swapping on 2 GB VPS | Newsletter send overlapped with other work | Add a swap file: sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile or upgrade to Professional (8 GB). |
Viewing Logs
# Last 50 lines of Ghost application logs
ghost log -n 50Live tail of the systemd journal for Ghost
sudo journalctl -u ghost_blog-example-com -fNginx access and error logs
sudo tail -f /var/log/nginx/blog.example.com.access.log
sudo tail -f /var/log/nginx/blog.example.com.error.logFAQ
Why does Ghost require Node 20 and not the newest Node version?
Ghost pins to even-numbered Node.js LTS releases for stability and ecosystem compatibility. Ghost 5.x supports Node 18 LTS and Node 20 LTS. Installing Node 21 or 22 will cause ghost install to refuse to proceed, and ghost doctor will flag the mismatch. Always install the active LTS version the Ghost docs list at ghost.org/docs/faq/node-versions.
Do I need MySQL, or can I run Ghost with SQLite?
Ghost ships SQLite for local development only. Every production deployment must use MySQL 8. Ghost-CLI enforces this during ghost install --production and refuses to start in production mode with SQLite. MariaDB is not officially supported for Ghost 5.x — stick with MySQL 8 to stay on the supported path and keep ghost doctor happy.
How do I enable paid subscriptions on Ghost?
Ghost has built-in membership and subscription functionality powered by Stripe. In Ghost Admin, open Settings → Membership → Stripe and connect your Stripe account with the secret key, then create monthly and yearly price tiers under Settings → Tiers. Ghost handles signup portals, checkout, customer accounts, invoicing, and webhook reconciliation automatically. You keep 100% of revenue minus Stripe's standard processing fee — Ghost itself takes no platform cut, unlike Substack's 10%.
Can Ghost send newsletters to thousands of members from a VPS?
Yes, but you must connect a transactional email provider for bulk email. Mailgun is the officially supported bulk provider — it is preconfigured in Ghost's email settings and handles list chunking, unsubscribe suppression, and bounce tracking automatically. For the much smaller volume of transactional email (password resets, signup confirmations), any SMTP provider works. Never try to send newsletters directly from your VPS's Postfix — you will land in Gmail's spam folder within the first batch.
How does Ghost-CLI manage the Ghost process on a server?
Ghost-CLI's systemd integration writes a systemd unit file that starts the Ghost Node process as the ghost system user. You manage it with ghost start, ghost stop, ghost restart, or the standard systemctl status ghost_yourdomain-com commands. Logs are written to the install directory's content/logs folder and also accessible via journalctl -u ghost_yourdomain-com -f. systemd also ensures Ghost restarts automatically after a crash or server reboot.
How do I update Ghost to a new version?
From the install directory (cd /var/www/ghost), run ghost update to upgrade to the latest version in the same major release series. Ghost-CLI backs up the current version to versions/, runs database migrations, starts the new version, and rolls back if anything fails. For major version jumps (Ghost 4 → 5, or 5 → 6), read the upgrade guide at ghost.org/docs/faq/upgrading first — breaking theme or integration changes may need attention before you update.
Should I use Ghost(Pro) or self-host Ghost on a VPS?
Ghost(Pro) is the managed SaaS run by the Ghost Foundation — it funds open-source development directly and is the easiest path for non-technical publishers. Self-hosting on a VPS costs far less at scale (EUR 7.99/month for a Starter VPS vs. USD 31/month minimum for Ghost(Pro) Starter), gives you full control over theming and server integrations, and avoids the member-count-based pricing tier jumps that Ghost(Pro) enforces. Pick self-hosted if you have basic Linux skills and plan to grow past a few hundred members, or Ghost(Pro) if you value zero ops over cost.
Next Steps
Now that Ghost is running on your VPS, here are the highest-leverage next moves:
- Set up staging with a second Ghost install. Clone the current install to
staging.example.comon the same VPS withghost installin a separate directory. Test theme changes, integration changes, and Ghost updates there before touching production.
- Integrate privacy-first analytics. Install Plausible or Umami on the same VPS for GDPR-compliant pageview tracking without cookie banners. Paste the tracking snippet into Ghost Admin → Settings → Code injection → Site header.
- Add Cloudflare in front of Nginx. Put Cloudflare's CDN and WAF in front of your Ghost install for DDoS protection and global edge caching of static assets. Set SSL mode to Full (strict) so Cloudflare validates the Let's Encrypt cert on your origin.
- Offload images to S3-compatible storage. Install the
ghost-storage-adapter-s3plugin to push uploaded images to Backblaze B2, Wasabi, or Cloudflare R2 instead of the local disk. This keeps your VPS disk lean and scales the filesystem independently.
- Build a headless frontend with the Content API. Ghost exposes a JSON Content API that any modern framework (Next.js, Astro, Nuxt) can consume. Use Ghost as the authoring backend and render the public site with a static site generator for lightning-fast page loads.
- Enable two-factor auth on staff accounts. Under Settings → Security → 2FA, enforce TOTP for the Owner account at minimum.
- Related install guides. WordPress on Ubuntu 24.04 for a general-purpose CMS alternative, Strapi on Ubuntu 24.04 for a headless CMS for custom apps, Directus on Ubuntu 24.04 for a data-first CMS, Nginx on Ubuntu 24.04 for deep-diving the reverse-proxy layer, and MariaDB on Ubuntu 24.04 if you need a general SQL database for another app on the same server.
Skip the Manual Install — Deploy Ghost in 60 Seconds>
Our VPS plans come with Node 20, MySQL 8, Nginx, and Ghost-CLI pre-configured. Launch a CloudCore VPS and follow a three-command ghost install to be live in under a minute.
>
- Ghost 5.x latest release pre-tested on every plan
- MySQL 8 tuned for Ghost's query patterns
- Nginx + Let's Encrypt auto-renewal via acme.sh
- Daily off-site backups of database and content>
Deploy Your Ghost VPS Now — Plans start at EUR 7.99/month.