If you are planning to host a Ghost blog or use Ghost as a headless blogging platform, you quickly notice it behaves differently from classic PHP-based systems like WordPress. Ghost runs on Node.js, listens on its own port, and expects you to put a reverse proxy and SSL in front of it. When this setup is done properly, you get a very fast, modern publishing stack that can serve a traditional blog, a headless content API, or both at the same time. In this article, we will walk through how we at dchost.com typically design and deploy Ghost: choosing the right server, installing Node.js and Ghost, putting Nginx in front as a reverse proxy, enabling HTTPS with SSL, and integrating Ghost into headless architectures where a separate frontend (for example Next.js or Nuxt) consumes its content API. The goal is to give you a practical, copy‑paste‑friendly blueprint you can reuse on your own VPS or dedicated server.
İçindekiler
- 1 Why Ghost and Headless Blogging Need a Different Hosting Approach
- 2 Planning the Server: Resources, OS and Network Layout
- 3 Installing Node.js, Ghost-CLI and Running Ghost as a Service
- 4 Putting Nginx In Front as a Reverse Proxy
- 5 Enabling HTTPS and HTTP/2 with SSL
- 6 Using Ghost as a Headless Blogging Platform
- 7 Production Hardening: Process Management, Logs, Backups and Monitoring
- 8 Putting It All Together and Next Steps
Why Ghost and Headless Blogging Need a Different Hosting Approach
Ghost is designed as a modern publishing platform with performance and clean content APIs in mind. That comes with a few implications for hosting:
- It runs on Node.js, not PHP. So traditional shared hosting optimized for PHP/MySQL is usually not a good fit.
- It listens on an internal port (by default 2368) and expects you to put a web server like Nginx in front as a reverse proxy.
- It has a built‑in JSON Content API, which makes Ghost perfect as a headless CMS feeding one or more frontends.
- It benefits a lot from proper SSL and HTTP/2, especially when you start serving images, scripts and multiple frontends.
In our daily work at dchost.com, we see two main Ghost scenarios:
- Classic Ghost blog on its own domain like blog.example.com (or as the main site), served directly by Ghost, with Nginx + SSL in front.
- Headless Ghost where Ghost only provides the content API and admin panel, while a separate frontend (Next.js, Nuxt, a mobile app, etc.) renders pages, often deployed on the same VPS or on another server.
Both scenarios share the same foundation: a clean Node.js runtime, a process manager or systemd service, Nginx as reverse proxy, and an HTTPS setup that is easy to maintain. If you are new to Node.js hosting, our article hosting Node.js and Express applications on shared hosting vs VPS vs serverless gives useful background on when a VPS or dedicated server is the right choice.
Planning the Server: Resources, OS and Network Layout
Choosing VPS vs dedicated vs colocation
For most Ghost blogs and headless setups, a Linux VPS is the sweet spot: enough control to run Node.js and Nginx, but still easy to manage. At dchost.com we typically recommend:
- Small personal or company blog: 1 vCPU, 1–2 GB RAM, fast SSD/NVMe storage.
- Medium‑size publication or agency blog with thousands of visitors per day: 2–4 vCPU, 4–8 GB RAM.
- Multi‑language headless setup with multiple frontends and heavy API traffic: 4+ vCPU, 8+ GB RAM, often on a larger VPS or a dedicated server.
If you expect very large traffic peaks, a dedicated server or colocation at our data centers lets you scale CPU and storage further, and combine Ghost with additional services (CDN origin, search, analytics) on the same physical machine.
Operating system and base hardening
We usually start with a fresh Ubuntu LTS or Debian image on the VPS or dedicated server. The general first‑day setup is similar to what we describe in our guide on what to do in the first 24 hours on a new VPS:
- Update system packages and security patches.
- Create a non‑root user with sudo access.
- Harden SSH access (key‑based login, non‑standard port if you prefer, disable root login).
- Enable a simple firewall (ufw or firewalld) with only SSH and HTTP/HTTPS open.
Network and DNS layout
Before installing Ghost, decide how you want your domains and subdomains to look. Common patterns include:
- Main site on Ghost: example.com points to Ghost (via Nginx reverse proxy).
- Blog as a subdomain: blog.example.com for Ghost, www.example.com for your main site or app.
- Headless API subdomain: content.example.com or ghost.example.com serving only the admin panel and JSON API; frontend lives elsewhere.
On your domain registrar, set A (and optionally AAAA) records to the VPS IP. If you are unsure how DNS pieces fit together, our article on how domain, DNS, server and SSL work together gives a practical overview.
Installing Node.js, Ghost-CLI and Running Ghost as a Service
Preparing the system for Ghost
Ghost runs on Node.js and is easiest to manage via the official Ghost‑CLI tool. A typical base setup on Ubuntu/Debian looks like this:
sudo apt update && sudo apt upgrade -y
sudo apt install -y nginx mysql-server
# Optional: secure MySQL quickly
sudo mysql_secure_installation
Ghost officially supports specific Node.js LTS versions. We usually install Node.js from the NodeSource repository or nvm. For a system‑wide installation using NodeSource:
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs
node -v
npm -v
Creating a directory and system user for Ghost
We like to keep Ghost isolated under /var/www and run it as a dedicated user:
sudo mkdir -p /var/www/ghost
sudo useradd -r -U -d /var/www/ghost ghost
sudo chown ghost:ghost /var/www/ghost
Installing Ghost-CLI and Ghost itself
Ghost‑CLI simplifies installation, upgrades and systemd service management. Install it globally:
sudo npm install -g ghost-cli@latest
Then switch to the Ghost directory as the ghost user and run the installer:
sudo -u ghost -H bash
cd /var/www/ghost
ghost install
Ghost‑CLI will ask a series of questions:
- Blog URL (e.g. https://blog.example.com)
- MySQL connection details
- Whether to set up systemd to manage the service
- Whether to configure Nginx automatically
- Whether to set up SSL using Let’s Encrypt
You can let Ghost‑CLI handle Nginx and SSL automatically, or you can choose to skip those parts and configure them manually. In this article we will walk through a manual Nginx and SSL setup so you fully understand what is going on behind the scenes.
After the installer runs, Ghost will typically be running on an internal port (for example 2368) and managed by systemd as a service named something like ghost_blog-example-com. You can check its status with:
sudo systemctl status ghost_blog-example-com
Ghost is now reachable on http://127.0.0.1:2368. The next step is to expose it to the outside world safely via Nginx.
Putting Nginx In Front as a Reverse Proxy
What a reverse proxy does in a Ghost setup
A reverse proxy sits in front of your application server and forwards incoming HTTP/HTTPS requests to it. With Ghost, the typical pattern is:
- Clients connect to port 80/443 on Nginx.
- Nginx terminates SSL (for HTTPS), handles HTTP/2, compression, caching headers, and routing.
- Nginx forwards requests to Ghost on 127.0.0.1:2368.
This design has several benefits:
- You can host multiple sites (Ghost, API, other apps) on the same server, each on different domains or paths.
- Nginx can handle static assets, gzip/Brotli, and HTTP/2/3 more efficiently than a raw Node.js process.
- You get a clean place to manage redirects, HSTS, security headers, and other HTTP niceties.
If you want a broader overview of reverse proxy concepts and examples, our guide on Nginx reverse proxy and simple load balancer setup for small projects walks through several scenarios very similar to Ghost.
Basic Nginx server block for Ghost (HTTP only, first step)
Let us start with a simple HTTP server block (we will add SSL in the next section). Create a new file like /etc/nginx/sites-available/blog.example.com.conf:
server {
listen 80;
listen [::]:80;
server_name blog.example.com;
# Optional: redirect www blog to non-www
# if ($host = 'www.blog.example.com') {
# return 301 $scheme://blog.example.com$request_uri;
# }
# Proxy all requests to Ghost
location / {
proxy_pass http://127.0.0.1:2368;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Host $host;
# Increase timeouts slightly for admin operations
proxy_read_timeout 90;
proxy_connect_timeout 90;
proxy_send_timeout 90;
}
}
Enable the site and test the Nginx configuration:
sudo ln -s /etc/nginx/sites-available/blog.example.com.conf
/etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
At this point, http://blog.example.com should show your Ghost blog, but without HTTPS yet. Leave it this way for a few minutes; we will layer SSL on top in the next step.
Enabling HTTPS and HTTP/2 with SSL
Why SSL matters even for a simple blog
Even if you are just running a personal blog, HTTPS is no longer optional. Browsers warn users on insecure login forms, search engines prefer HTTPS, and features like HTTP/2/3 require TLS. When we design hosting for clients, we default everything to HTTPS, including staging sites.
You can use a free Let’s Encrypt certificate or a commercial certificate, depending on your needs. If you want a deeper comparison, we explain the trade‑offs in our article about Let’s Encrypt vs commercial SSL certificates. For most Ghost blogs, Let’s Encrypt is more than sufficient.
Obtaining a Let’s Encrypt certificate with Certbot
The easiest way on Ubuntu/Debian with Nginx is to use Certbot. Install it from the OS repository or the official snap:
sudo apt install -y certbot python3-certbot-nginx
Then run:
sudo certbot --nginx -d blog.example.com
Certbot will:
- Verify that
blog.example.compoints to this server. - Request a certificate from Let’s Encrypt.
- Update your Nginx configuration with SSL directives.
- Optionally set up an automatic HTTP to HTTPS redirect.
After this step, your Nginx server block will have additional lines like:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name blog.example.com;
ssl_certificate /etc/letsencrypt/live/blog.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# ... your proxy_pass config ...
}
server {
listen 80;
listen [::]:80;
server_name blog.example.com;
return 301 https://$host$request_uri;
}
Certbot also installs a cron job or systemd timer for automatic renewal. You can test it with:
sudo certbot renew --dry-run
If you want to go deeper into HTTPS best practices (HSTS, redirect strategies, mixed content), our guide on full HTTPS migration with 301 redirects, HSTS and SEO‑safe setup walks through a full migration story that applies equally well to Ghost.
Modern TLS and performance tuning
Once HTTPS is in place, we usually add a few extra touches:
- Enable HTTP/2 (already done by
http2in the Nginx listen directive). - Turn on gzip or Brotli compression for HTML, CSS, JS and JSON API responses.
- Optionally add HSTS once you are confident everything works over HTTPS only.
Most of these can be handled in a small Nginx include file shared by your sites. For an even more performance‑focused setup, especially when combining TLS 1.3, OCSP stapling and Brotli, see our practical guide on configuring TLS 1.3, OCSP stapling and Brotli on Nginx.
Using Ghost as a Headless Blogging Platform
Ghost’s Content API in practice
Ghost exposes a Content API and an Admin API over HTTP. This makes it a very capable headless CMS for blogs, magazines and documentation sites. Typical headless use cases include:
- Serving a blog frontend built with Next.js, Nuxt, Gatsby, Astro or another static/SSR framework.
- Sharing content between a website and a mobile app.
- Publishing posts to multiple brands or domains from a single Ghost instance.
From a hosting perspective, nothing changes on the Ghost side: it still runs as a Node.js service on an internal port and is proxied by Nginx. The difference is how your frontend connects to it and how you structure routing on the reverse proxy.
Routing patterns for headless Ghost
There are two common patterns we implement for clients.
1. Separate subdomains for frontend and Ghost
- Ghost admin and API at
ghost.example.com. - Public frontend at
blog.example.comorwww.example.com.
In this model, Ghost is never directly visible to visitors. Only your editors log in to ghost.example.com, and your frontend consumes the Content API with an API key. Each subdomain has its own Nginx server block, SSL certificate and caching rules.
2. Single domain, path‑based routing
- Public frontend handles everything under
/. - Ghost admin and API live under
/ghost/or/content/.
Here, Nginx decides by path which upstream to send traffic to: your frontend (for example a Node.js SSR app) or the Ghost service. A simplified example:
server {
listen 443 ssl http2;
server_name example.com;
# SSL directives omitted for brevity
# Frontend (Next.js) upstream
location / {
proxy_pass http://127.0.0.1:3000;
# ... proxy headers ...
}
# Ghost admin + API
location /ghost/ {
proxy_pass http://127.0.0.1:2368;
# ... proxy headers ...
}
}
This lets your editors access Ghost at https://example.com/ghost/ while visitors see only the frontend. If you want a broader overview of headless and Jamstack patterns (including static builds and object storage origins), our guide on hosting headless CMS and Jamstack sites is a good companion read.
Where to run the frontend
Depending on your scale and team preferences, you can:
- Run the frontend on the same VPS or dedicated server as Ghost (simple, cost‑effective for small to medium projects).
- Use a separate VPS for the frontend if it needs more CPU or has different scaling requirements.
- Build a fully static frontend and serve it from object storage + CDN, using Ghost only as a content source for builds.
Whatever you choose, the key is to keep your architecture simple at first and only split components when you truly need extra isolation or scaling headroom.
Production Hardening: Process Management, Logs, Backups and Monitoring
Process management and zero‑downtime deploys
If you used Ghost‑CLI, it already set up a systemd service. For custom Node.js headless frontends that live next to Ghost, we recommend using systemd units or a process manager like PM2 and an Nginx reverse proxy in front. We describe a practical, no‑drama pattern in our article on hosting Node.js in production with PM2/systemd, Nginx, SSL and zero‑downtime deploys.
For Ghost itself, upgrades are usually handled by Ghost‑CLI with commands like ghost update, which gracefully restarts the process via systemd.
Logging and diagnostics
When running Ghost and potentially a separate frontend, keep an eye on three log sources:
- Ghost logs (under
/var/www/ghost/content/logs/by default). - Nginx access and error logs under
/var/log/nginx/. - Systemd journal for Ghost services (
journalctl -u ghost_...).
For growing projects, centralised log aggregation (for example with Loki + Promtail or ELK) helps a lot, especially when you add more servers later.
Backups and disaster recovery
Ghost stores content primarily in a MySQL/MariaDB database and in its content/ directory (images, themes, configuration). A minimal backup plan should include:
- Regular database dumps (mysqldump or XtraBackup) to off‑site storage.
- File backups of the
/var/www/ghost/content/directory. - Configuration backups for Nginx and systemd units.
If you are designing a broader backup policy for multiple sites and apps, our 3‑2‑1 backup article on why the 3‑2‑1 backup strategy works and how to automate backups on cPanel, Plesk and VPS is a good framework to start with.
Security basics
Beyond SSL and updated software, a secure Ghost and headless setup should include:
- Firewall rules that only allow ports 22, 80 and 443 from the internet. Ghost should only listen on 127.0.0.1.
- Regular OS and package updates, including Node.js security patches.
- Strong passwords and 2FA for Ghost admin users where possible.
- Optionally a Web Application Firewall (WAF), either at the CDN level or on the server (for example ModSecurity in front of Nginx).
For a deeper security checklist around VPS hardening (SSH, firewalls, Fail2ban and more), our guide on how to secure a VPS server without drama provides a step‑by‑step path you can follow for the same server that hosts Ghost.
Putting It All Together and Next Steps
Hosting a Ghost blog or a headless Ghost‑powered publishing stack is not complicated once you see the full picture. You need a clean Node.js runtime, a service manager (systemd or PM2), Nginx as a reverse proxy, and an SSL setup that is automated and well‑understood. On top of this foundation, you can decide whether Ghost will render the blog itself, or whether it will only provide content via its APIs to a separate frontend built with modern frameworks.
At dchost.com we typically start clients on a VPS with enough CPU, RAM and NVMe storage to handle both Ghost and, if needed, a Node.js frontend on the same machine. When the project grows, it is easy to split roles: a dedicated Ghost instance as a pure headless CMS, multiple frontends on separate servers, object storage for media and a CDN in front of everything. The reverse proxy and SSL layer you built for the first small blog still scales nicely in those more advanced architectures.
If you are planning your own Ghost deployment and want help sizing a VPS or dedicated server, or designing a headless architecture that fits your content and traffic patterns, our team at dchost.com can walk through your requirements and propose a concrete, no‑surprises plan. Whether you just need a fast, minimal Ghost blog or a multi‑frontend headless publishing platform, the steps in this guide will give you a solid, production‑ready foundation.
