GICT knowledge base
Run Blog & Newsletters with Ghost: Complete Ubuntu 24.04 Setup Guide
Published
Every time a Substack writer collects a paid subscription, Substack keeps 10%. A writer earning $500/month from their newsletter pays Substack $600/year. Forever. That number grows with your success. Ghost does not take a cut. Ghost is open source software you run on your own server. You pay once for the VPS and nothing else, ever.
The self-hosted version of Ghost has every feature Substack offers. Paid subscriptions, email newsletters, member management, premium content gating, built-in analytics. The difference is that your subscriber list belongs to you. If Substack changes its policy tomorrow, you have no recourse except to leave. If you self-host Ghost and the worst happens, your list is still on your server in a MySQL dump.
Here is how to deploy Ghost on Ubuntu 24.04. It takes about 30 minutes if you follow the steps in order.
What You Need Before Starting
- Ubuntu 24.04 VPS with at least 1GB RAM (2GB recommended) – Get it on InterServer at $3 per month!
- A registered domain with an A record pointing at your VPS IP
- SSH access to the server
Ghost needs a domain for its SSL certificate setup. Certbot will verify domain ownership automatically during installation, so DNS must be active before you begin. If you do not have a domain yet, use Porkbun or Cloudflare to register one and create the A record before continuing.
If you are testing without a domain, nip.io works as a free wildcard DNS service. Any subdomain like ghost.YOUR_VPS_IP.nip.io resolves to your VPS IP. Replace YOUR_VPS_IP with your actual IP using dashes, not dots. For example, if your IP is 49.13.74.134, use ghost.49-13-74-134.nip.io. Note that SSL certificates from Let’s Encrypt do not work with nip.io, so you will need to accept a browser SSL warning or use a real domain for production.
Step 1: Install Node.js v22
Ghost runs on Node.js. The Ubuntu default repository has an old version. Use NodeSource to get the current LTS release:
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --batch --yes --dearmor -o /etc/apt/keyrings/nodesource.gpg
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
sudo apt update
sudo apt install -y nodejs
node --version
npm --version
You should see Node.js 22.x and npm 10.x in the output. Keep these numbers in mind.
Step 2: Install MySQL
Ghost stores all your content, members, and subscription data in MySQL. Install it with:
sudo apt install -y mysql-server
And execute the following command to secure your mysql installation.
mysql_secure_installation
Answer the questions and enter the root password if asked. Once done, You are ready to move forward to step 3.
Step 3: Create the Database and User
Create a dedicated MySQL database and user for Ghost. Run each line separately:
sudo mysql -e "CREATE DATABASE ghost_prod CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
sudo mysql -e "CREATE USER 'ghost'@'localhost' IDENTIFIED BY 'GhostUserPassword456';"
sudo mysql -e "GRANT ALL PRIVILEGES ON ghost_prod.* TO 'ghost'@'localhost'; FLUSH PRIVILEGES;"
Replace GhostUserPassword456 with a strong password and record it. Ghost will ask for the MySQL username and password during setup.
Step 4: Install nginx
Ghost uses nginx as its reverse proxy web server. Install it and open the necessary ports:
sudo apt install -y nginx
sudo ufw allow 'Nginx Full'
Verify nginx is running:
sudo systemctl status nginx
Look for “active (running)” in the output.
Step 5: Create the Ghost System User
Run Ghost under its own system user, not as root. Ghost-CLI refuses to run as root. Create the user with this command:
sudo useradd -m -s /bin/bash -G sudo ghost
echo "ghost:GhostLoginPassword789" | sudo chpasswd
Do not use adduser here. adduser is interactive and will hang when you run it over SSH without a TTY.
Immediately after creating the user, run this:
sudo chmod 755 /home/ghost
This step is required. Ghost-CLI checks that the ghost user home directory is readable before installation begins. Without this permission set, ghost install fails with “The directory /home/ghost is not readable by other users on the system.”
Step 6: Install Ghost-CLI
Install the Ghost command line tool globally via npm:
sudo npm install -g ghost-cli@latest
Here is the part that trips up almost everyone. When Node.js is installed via NodeSource, npm lives in a non-standard location that sudo does not inherit. sudo ghost fails with “command not found” even though the install succeeded. Fix it with a symlink:
sudo ln -sf $(npm bin -g)/ghost /usr/local/bin/ghost
sudo ghost --version
The version number should print without errors.
Step 7: Fix Node Version Conflicts
Check if your system already has an older version of Node installed:
node --version
If you see a version below 22 (for example, v18.x from Ubuntu’s default repository), ghost-cli may fail later with a pnpm error. Hide the system node so only the newer version is active:
sudo mv /usr/bin/node /usr/bin/node.system.bak
node --version
After running this, node --version should show v22.x. If it does not, restart your SSH session and try again.
Step 8: Run Ghost Install
Switch to the ghost user and navigate to the install directory:
sudo -u ghost -i
cd /var/www/ghost
Run the installer:
ghost install
The installer asks a series of questions. Here is the full list with the correct answers:
- Blog URL: Enter your full domain with https, for example
https://yourblog.com - MySQL hostname: Press Enter to accept
localhost - MySQL username:
ghost - MySQL password: The password you set in Step 3
- Ghost database name:
ghost_prod - Set up a Ghost MySQL user: Yes
- Configure nginx: Yes
- Set up SSL: Yes (provide your email for certificate renewals)
- Set up systemd: Yes
- Start Ghost: Yes
Installation takes 2-5 minutes depending on your server speed.
Important: ghost install runs internal commands that require a TTY. If you are connected over SSH without a pseudo-terminal (for example, using ssh user@host 'command' without -t), the install fails with “Prompts have been disabled.” To work around this, use ssh -t user@host to get a proper terminal, or use the manual setup method described below.
Ghost instance installed successfully!
Url: https://yourblog.com
Admin: https://yourblog.com/ghost
Save the admin URL. The first time you visit it, Ghost prompts you to create your owner account.
If SSL fails with a DNS error, your domain A record may not have propagated yet. Run ghost setup ssl after DNS confirms working, or test with curl -k https://your-domain.com to bypass the certificate warning temporarily.
If ghost install fails partway through with “Prompts have been disabled,” create the config file manually and start Ghost directly:
sudo -u ghost cat > /var/www/ghost/config.production.json << 'EOF'
{
"url": "https://yourblog.com",
"server": { "port": 2368, "host": "127.0.0.1" },
"database": {
"client": "mysql",
"connection": {
"host": "localhost",
"user": "ghost",
"password": "GhostUserPassword456",
"database": "ghost_prod"
}
},
"mail": { "transport": "Direct" },
"logging": { "level": "info" },
"process": "systemd",
"paths": { "contentPath": "/var/www/ghost/content" }
}
EOF
sudo chown -R ghost:ghost /var/www/ghost/config.production.json
sudo -u ghost NODE_ENV=production node /var/www/ghost/current/index.js &
Step 9: Complete Your Publication Setup
Open your browser and go to https://yourdomain.com/ghost. Create your owner account with your name, email, site title, and a password.
Once logged in, the Ghost admin panel has these key sections:
- Posts: Write and publish articles using the Markdown editor
- Members: View and manage your subscriber list, paid members, and free subscribers
- Tiers: Create paid membership levels with different price points
- Email newsletters: Draft an email, preview it, and send it directly to your full list
- Analytics: See traffic, subscriber growth, and paid conversion rates
Ghost automatically generates an RSS feed, XML sitemap, and JSON schema markup for SEO on every post.
Configure Email Sending
Ghost needs an email service to send newsletters and member notifications. Without this, new signups do not receive confirmation emails and you cannot send bulk newsletters.
Mailgun offers 5,000 free emails per month. Register at Mailgun, add your domain, and note your SMTP credentials. In the Ghost admin panel, go to Settings, then Email newsletters, and enter:
- SMTP hostname:
smtp.mailgun.org - SMTP username:
[email protected] - SMTP password: your Mailgun SMTP password
- Port: 587
Save and send a test email to verify delivery.
Set Up Paid Subscriptions
Navigate to Settings, then Memberships, and enable paid memberships. Ghost has Stripe built in for payment processing. Connect your Stripe account, create at least one tier (for example, $7/month for premium posts), and publish your publication.
Ghost handles the entire subscription lifecycle. Stripe payments, failed payment retries, member upgrades and cancellations, and delivery of premium content. You never touch a payment spreadsheet.
The key difference from Substack is your revenue. Substack charges 10% of paid subscriptions. Ghost takes nothing. Your Stripe fees (2.9% plus 30 cents per transaction) go to Stripe, not to a platform.
Keep Ghost Updated
Update Ghost to the latest version with one command:
cd /var/www/ghost
ghost update
Ghost CLI checks the current version, downloads the update, runs database migrations if needed, and restarts the service. Back up your content regularly by exporting from the admin panel under Settings, then Labs, then Export.
Ready to stop paying platform fees? InterServer VPS plans start at $6/month with the resources needed to run Ghost. Full root access, unmetered bandwidth, and Ubuntu 24.04 ready to go.
Related guides
How to Self-Host DocuSeal for Document Signing
Self-host DocuSeal with private administrator setup, PostgreSQL and HTTPS. Test signing, understand email data flows and create consistent backups.
Self HostedHow to Self-Host Docmost with Docker on Ubuntu 24.04
Install Docmost with Docker, PostgreSQL and Redis. Set up a private workspace and verify editing, live updates and attachment retrieval.
Self HostedHow to Remove Image Backgrounds Locally with rembg
Remove image backgrounds locally with rembg. Preserve originals, check PNG transparency and inspect cutout quality on both light and dark backgrounds.
Self HostedHow to Translate Text Offline with Argos Translate
Translate plain text locally with Argos Translate. Install a language pair, prepare caches, verify offline operation and review translation quality.
