Install and maintain Ghost on Ubuntu
Ghost is installed non-interactively with Node.js 22, MariaDB, the Ghost CLI, a dedicated system user, a custom systemd service, Nginx, and TLS.
How this guide was prepared: This is the command-line equivalent of the current Sive AppStore installation playbook. It covers the application installation and the parts you maintain after deployment. Platform provisioning, billing integration, and one-time orchestration are intentionally omitted.
Before you start
- Use a clean, supported Ubuntu server with root or sudo access.
- Point
app.example.comto the server before requesting a public TLS certificate. - Replace every value written as
CHANGE_MEand store the generated credentials in a password manager. - Take a snapshot before changing an existing installation.
- A dedicated MariaDB database/user/password and a domain.
- An email delivery provider configured after installation if newsletters or staff invitations are required.
Important: This playbook-aligned deployment uses MariaDB, not an interactive MySQL install. Keep the Ghost config and database credentials readable only by the Ghost service account/root.
What the AppStore installation creates
- Node.js 22 and global
ghost-cli - MariaDB database and dedicated
ghostLinux user - Ghost below
/var/www/app.example.com/ghoston port 2368 - Custom systemd service, Nginx, Certbot, UFW, and Fail2ban
1. Install Node.js 22, MariaDB, and Nginx
sudo apt update
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo bash -
sudo apt install -y nodejs mariadb-server nginx certbot python3-certbot-nginx ufw fail2ban
sudo npm install -g ghost-cli
sudo systemctl enable --now mariadb nginx
2. Create the database and service user
sudo mysql
CREATE DATABASE ghost CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'ghostuser'@'localhost' IDENTIFIED BY 'CHANGE_ME_DB_PASSWORD';
GRANT ALL PRIVILEGES ON ghost.* TO 'ghostuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;
sudo useradd --system --home /var/www/app.example.com/ghost --create-home --shell /bin/bash ghost
sudo chown -R ghost:ghost /var/www/app.example.com
3. Install Ghost non-interactively
Run Ghost CLI as the dedicated user and let the custom systemd/Nginx steps manage services.
sudo -iu ghost
cd /var/www/app.example.com/ghost
ghost install --no-prompt --no-setup-linux-user --no-setup-nginx --no-setup-ssl --no-setup-systemd \
--url https://app.example.com \
--db mysql --dbhost localhost --dbname ghost --dbuser ghostuser --dbpass 'CHANGE_ME_DB_PASSWORD'
exit
4. Create systemd, Nginx, and TLS
Create a systemd unit for the Ghost production process on localhost port 2368, then proxy it through Nginx.
sudo nano /etc/systemd/system/ghost-app-example-com.service
sudo systemctl daemon-reload
sudo systemctl enable --now ghost-app-example-com
sudo nano /etc/nginx/sites-available/ghost-app-example-com
sudo ln -s /etc/nginx/sites-available/ghost-app-example-com /etc/nginx/sites-enabled/ghost-app-example-com
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d app.example.com -m admin@example.com --agree-tos --redirect
Important files and data
- Ghost installation and content:
/var/www/app.example.com/ghost - Ghost production config:
config.production.json - Service:
/etc/systemd/system/ghost-app-example-com.service - Nginx site and MariaDB database
Health checks and logs
Run these checks after installation and after each upgrade:
sudo systemctl status ghost-app-example-com mariadb nginx --no-pager
sudo journalctl -u ghost-app-example-com -n 100 --no-pager
curl -I http://127.0.0.1:2368
sudo nginx -t
Routine maintenance
Review release notes and take a backup or snapshot before upgrading. Use the following playbook-aligned commands as the starting point:
sudo -iu ghost
cd /var/www/app.example.com/ghost
ghost backup
ghost update
exit
sudo systemctl status ghost-app-example-com --no-pager
Backup scope
- MariaDB Ghost database
- Ghost
contentdirectory andconfig.production.json - Systemd, Nginx, TLS, and mail-provider configuration
A usable backup needs both application files and application data. Test restoration on a separate server; an untested backup is not a recovery plan.
Troubleshooting
- Confirm DNS with
dig +short app.example.combefore retrying Certbot. - Test the web-server configuration before reloading it:
sudo nginx -torsudo apache2ctl configtest. - Check free space with
df -hand listening ports withsudo ss -ltnup. - If a service fails, inspect its systemd journal before changing configuration.
- Ghost calls its MariaDB/MySQL-family driver
mysql; that does not mean the host must run Oracle MySQL. - If Ghost CLI and the custom systemd unit disagree about service state, use systemd as the source of truth for this deployment.
Security notes
- Do not paste passwords, API keys, repository credentials, private keys, or access tokens into tickets or public logs.
- Expose only the documented public ports. Keep database and application backend ports bound to localhost or a private network.
- Keep SSH access working before enabling UFW, then allow only the ports this guide lists.
- Renewal can be tested safely with
sudo certbot renew --dry-runwhere Certbot manages TLS.

