How-To Install Ghost on Ubuntu 16.04

How-To Install Ghost on Ubuntu 16.04


Ghost is the blogging platform that I use for this site. It's a light-weight, fast and fully customizable alternative to WordPress.

Ghost is built with Node.js, while WordPress uses PHP. In benchmarking tests, Node.js consistently outperforms PHP by a lot.

Some of the features include:

  • AMP integration
  • Ghost Desktop App
  • Markdown content editing
  • RSS subscriptions
  • SEO friendly - canonical tags, permalinks, semantic markup, XML sitemap
  • Twitter Cards & Facebook Open Graph

In this guide I'll show you how to download, configure and run ghost as a system service and set-up a reverse proxy for it using Apache2 or Nginx.


  • Ubuntu 16.04 LTS
  • Apache2 or Nginx

How to install Ghost

Create a new user for Ghost to run under

sudo adduser --disabled-login --gecos 'Ghost blog' ghost

Add NodeSource Node.js v6.x repo

curl -sL | sudo -E bash -

Install Node.js v6.x

sudo apt-get install -y nodejs

Install knex-migrator

sudo npm install -g knex-migrator

Download Ghost

cd /var/www/
wget -O
unzip -d blog

Install Ghost

npm install --production --prefix /var/www/

Create the configuration file

nano config.production.json

Add your blog's details to the configuration file:

    "url": "https://your-website-url-here/",
    "server": {
        "host": "localhost",
        "port": 2368
    "database": {
        "client": "mysql",
        "connection": {
            "host"     : "localhost",
            "user"     : "your-username-here",
            "password" : "your-password-here",
            "database" : "your-database-name-here"
    "auth": {
        "type": "password"
    "paths": {
        "contentPath": "content/"
    "logging": {
        "level": "info",
        "rotation": {
            "enabled": true
        "transports": ["file", "stdout"]

Set permissions

sudo chown ghost:www-data -R /var/www/

Initialize the database

NODE_ENV=production knex-migrator init

Set-up a reverse proxy

Apache web server

Add your website's information to the configuration file

sudo nano /etc/apache2/sites-available/
<VirtualHost *:80>
     ProxyRequests off
     ProxyPass /
     ProxyPassReverse / http:/
     ErrorLog /var/www/logs/
     CustomLog /var/www/logs/ combined

Enable website in Apache

sudo a2ensite

Reload Apache

sudo service apache2 reload


Create a configuration file for your domain.

sudo nano /etc/nginx/sites-available/
server {
    listen 80;

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header HOST $http_host;
        proxy_set_header X-NginX-Proxy true;

        proxy_redirect off;

Create a symbolic link for the configuration file

sudo ln -s /etc/nginx/sites-available/ /etc/nginx/sites-enabled/

Make sure there's no syntax errors in your Nginx config file

sudo nginx -t

Restart Nginx

sudo service nginx restart

Add Ghost as a system service

sudo nano /etc/systemd/system/ghost.service

Add configuration details to file




ExecStart=/usr/bin/npm start --production
ExecStop=/usr/bin/npm stop --production


Start Ghost

sudo systemctl enable ghost.service
sudo systemctl start ghost.service

Your Ghost blog should now be up and running!


How do you run more than one Ghost blog on the same server?

It's simple. For each Ghost blog installation, you'll need to run it on its own unique port. You can specify the port in the config.production.json file.



I was getting a ETIMEDOUT error when trying to initialize the database.

I knew my credentials were correct and that the MySQL server was running. So what could it be? Why was it timing out when trying to connect to it?

I pinged localhost and sure enough that was the problem.

ping localhost
PING ( 56(84) bytes of data.

There was no entry for localhost inside of /etc/hosts. And I'm using Google's DNS servers, so they were resolving localhost to That's where it was trying to connect to.

A simple line in /etc/hosts fixed it: localhost

NODE_ENV=production knex-migrator init
[2017-09-07 16:40:09] ERROR

NAME: InternalServerError
MESSAGE: The server has encountered an error.

Error: connect ETIMEDOUT
    at Connection._handleConnectTimeout (/var/www/