Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Ghost Ubuntu
GUIDEInstall Guides

How to Install Ghost CMS on Ubuntu 24.04 — Self-Hosted Publishing with Memberships

31 min read

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?
  • Why Self-Host Ghost Instead of Ghost(Pro), Substack, or Medium?
  • Prerequisites
  • Step 1: Update System Packages
  • Step 2: Create a Non-Root Ghost User
  • Step 3: Install Node.js 20 LTS
  • Step 4: Install and Secure MySQL 8
  • Step 5: Install Nginx
  • Step 6: Install Ghost-CLI
  • Step 7: Run ghost install
  • Step 8: Configure Mailgun for Newsletters
  • Step 9: Enable Stripe Memberships
  • Step 10: Install and Customize the Casper Theme
  • Step 11: systemd Management, Updates, and Backups
  • Troubleshooting
  • FAQ
  • Next Steps
  • 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

    ScenarioGhost on CloudCore StarterGhost(Pro) CreatorSubstackMedium Partner
    Base monthly costEUR 7.99USD 56 (~EUR 52)USD 0USD 0
    Per-member feesNoneJumps at 1,000 / 2,000 / 10,000NoneNone
    Revenue share0% (you keep all)0% (you keep all)10%N/A (read-time pool)
    Member count at first price breakUnlimited1,000UnlimitedUnlimited
    Custom domainYesYesYesPaid plan only
    Custom themeYes (full Handlebars)Yes (full Handlebars)No (fixed)No (fixed)
    Own your email listYesYesYes (exportable)No
    Own the databaseYesNoNoNo
    Annual cost at USD 50k revenueEUR 107USD 672USD 5,000Pool-dependent
    For any publication earning more than a few hundred euros a month in subscriptions, self-hosting Ghost pays for itself within the first billing cycle. For a pre-revenue publication, the monthly cost is lower than a single Ghost(Pro) plan — and there is no subscriber cap forcing an upgrade.

    CloudCore Pricing Tiers for Ghost

    TierSpecsIdeal ForMonthly
    Starter2 vCPU, 2 GB RAM, 40 GB NVMeNew Ghost site, up to ~3,000 membersEUR 7.99
    Professional4 vCPU, 8 GB RAM, 100 GB NVMeActive publication, 10,000+ members, heavy newsletter sendsEUR 19.99
    Business8 vCPU, 16 GB RAM, 200 GB NVMeMulti-site Ghost, 50,000+ members, headless frontendEUR 39.99
    Self-hosting Ghost also gives you:

    • 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.
    If you want to weigh Ghost against other CMS and content-platform choices, see our install guides for WordPress on Ubuntu 24.04 (the general-purpose giant), Strapi on Ubuntu 24.04 (headless CMS for custom apps), and Directus on Ubuntu 24.04 (data-platform CMS). Ghost wins specifically when your product is a publication with paid subscribers.

    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 with dig +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:

    bash
    ssh root@your-server-ip

    Step 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.

    bash
    sudo apt update && sudo apt upgrade -y

    If a kernel upgrade occurred, reboot before continuing:

    bash
    sudo reboot

    Reconnect 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.

    bash
    sudo adduser ghost-mgr
    sudo usermod -aG sudo ghost-mgr

    Set a password when prompted and leave the rest blank. Switch to the new user:

    bash
    su - ghost-mgr

    From this point on, run every command as ghost-mgr with sudo when required.

    Why not just name the user ghost? Ghost-CLI creates its own system user named ghost during ghost install to 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:

    bash
    curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
    sudo apt install -y nodejs

    Verify the install:

    bash
    node --version
    

    v20.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.

    bash
    sudo apt install -y mysql-server

    Set 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):

    bash
    sudo mysql

    In the MySQL prompt:

    sql
    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:

    bash
    sudo mysql_secure_installation

    Create the Ghost database and user:

    bash
    sudo mysql -u root -p
    sql
    CREATE 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:

    • utf8mb4 is the only character set you should use. The older MySQL utf8 is a three-byte truncation that corrupts emoji and CJK.
    • localhost scoping means the credentials cannot be abused from the internet even if they leak.
    Ghost-CLI can also create this user itself during 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:

    bash
    sudo apt install -y nginx

    Allow HTTP and HTTPS through the firewall:

    bash
    sudo ufw allow 'Nginx Full'
    sudo ufw allow OpenSSH
    sudo ufw enable

    Confirm Nginx is running:

    bash
    sudo systemctl status nginx

    Visiting 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:

    bash
    sudo npm install -g ghost-cli@latest

    Verify the install:

    bash
    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:

    bash
    sudo mkdir -p /var/www/ghost
    sudo chown ghost-mgr:ghost-mgr /var/www/ghost
    sudo chmod 775 /var/www/ghost
    cd /var/www/ghost

    Ownership 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:

    bash
    ghost install

    Ghost-CLI launches an interactive wizard. Typical answers:

    text
    ? 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? Yes

    Behind the scenes, Ghost-CLI:

  • Downloads the latest Ghost release tarball and extracts it into /var/www/ghost/versions/X.Y.Z/
  • Runs the initial database migrations against ghost_production
  • Writes /var/www/ghost/config.production.json with your URL, DB credentials, and mail defaults
  • Generates an Nginx site file at /etc/nginx/sites-available/blog.example.com.conf
  • Uses acme.sh (bundled with Ghost-CLI, not Certbot) to request a Let's Encrypt certificate and writes the TLS block into the Nginx config
  • Writes a systemd unit at /lib/systemd/system/ghost_blog-example-com.service
  • Starts the Ghost process under systemd
  • Expected final output:

    text
    ✔ Configuring Ghost
    ✔ Setting up instance
    ✔ Running database migrations
    ✔ Configuring Nginx
    ✔ Configuring SSL
    ✔ Configuring systemd
    ✔ Starting Ghost

    Ghost 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:

    bash
    sudo crontab -l | grep acme

    sudo 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:

    bash
    cd /var/www/ghost
    nano config.production.json

    Add a mail block for transactional SMTP (Mailgun's SMTP endpoint works well here):

    json
    "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:

    bash
    ghost restart

    Bulk 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:

  • Open Settings → Membership → Stripe
  • Click Connect with Stripe
  • Paste the secret key
  • Ghost validates the key and creates the necessary Stripe webhook endpoint automatically at https://blog.example.com/members/webhooks/stripe
  • Next, 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
    Then configure the signup and checkout UX in Settings → Portal:

    • 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
    Every post now has a Access dropdown (Public / Members only / Paid members only / Specific tiers), and Ghost handles paywall, checkout, webhook reconciliation, customer portal, and invoice receipts end to end. Stripe takes its standard processing fee; Ghost takes nothing.

    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:

    bash
    git clone https://github.com/TryGhost/Casper.git my-theme
    cd my-theme
    npm install
    npm run zip

    Then 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

    bash
    # From inside /var/www/ghost — preferred, uses Ghost-CLI wrapper:
    ghost status
    ghost restart
    ghost stop
    ghost start
    ghost log

    Or via systemd directly:

    sudo systemctl status ghost_blog-example-com sudo journalctl -u ghost_blog-example-com -f

    Updating Ghost

    To upgrade to the latest Ghost version in the same major release:

    bash
    cd /var/www/ghost
    ghost update

    Ghost-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:

  • The MySQL database (ghost_production)
  • The content/ directory (images, theme uploads, routes.yaml, redirects)
  • config.production.json (credentials)
  • Back up all three nightly. Create /usr/local/bin/ghost-backup.sh:

    bash
    #!/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:

    bash
    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>&1

    Mirror /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

    ProblemCauseSolution
    Error: Your Node version is not supportedInstalled Node is not an even-numbered LTSUninstall current Node, reinstall Node 20 LTS from NodeSource, then retry ghost install.
    Error: Unable to connect to MySQL during installWrong MySQL password or MySQL not listeningVerify with mysql -u ghostuser -p. Confirm MySQL is running: sudo systemctl status mysql.
    MySQL auth failure: caching_sha2_passwordDefault MySQL 8 auth plugin not supported by Ghost-CLI probeALTER USER 'ghostuser'@'localhost' IDENTIFIED WITH mysql_native_password BY '…';
    Let's Encrypt certificate request failsDNS not pointing at server, or port 80 blockedConfirm 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 rootSecurity checkCreate a non-root sudo user (Step 2) and run the installer as that user.
    502 Bad Gateway from NginxGhost Node process crashedghost log --error. Common causes: MySQL down, OOM kill, config syntax error. Restart: ghost restart.
    Newsletter sends to 0 subscribersMailgun not configured or domain unverifiedRe-enter key in Settings → Email newsletter → Mailgun config. Verify DNS records in Mailgun dashboard.
    Stripe Checkout returns 500 errorWrong 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 errorDatabase permissions or corrupted migrationRun ghost doctor. Restore from your latest backup, then rerun the update.
    Image upload returns 413Nginx client_max_body_size too lowAlready 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 beensudo chown -R ghost:ghost /var/www/ghost/content, then ghost doctor.
    High memory, swapping on 2 GB VPSNewsletter send overlapped with other workAdd 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

    bash
    # Last 50 lines of Ghost application logs
    ghost log -n 50

    Live tail of the systemd journal for Ghost

    sudo journalctl -u ghost_blog-example-com -f

    Nginx 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.log

    FAQ

    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.com on the same VPS with ghost install in 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-s3 plugin 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.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket