The ghost blog project is pretty cool, it's also written in nodejs. This is not a bad thing but node is a relative newcomer to the world and can be a little intimidating to deploy to production, especially if you would like to share precious ports 80 and 443 (HTTP and HTTPS) on the server's IP with other sites.

To get this working requires virtualhosts to be setup, which is where your server looks at the URL of the site being requested and serves different content depending on what it sees. This way you can have on the same server as without getting all mixed up.

To further complicate matters ghost needs to run in it's own node instance, on it's own port and it has no idea about virtualhosts, so in order to enable virtualhosts we have to either modify the ghost code (making future updates much harder as we'd have to port our modifications) or put a proxy server in front of ghost and node that understands URL based host header routing. This configuration is known as a reverse proxy.

In this walkthrough we'll setup ghost listening on the localhost ip and then configure a reverse, virtualhost aware proxy using nginx to serve that content to the outside world.

Note that for virtualhosts to work you have to configure your DNS correctly as it's the domain name that is used by the server to select the correct content for your browser. There are lots of guides on how to configure DNS and it's a bit beyond the scope of this page, but none of this will work until you can:


and get replies from the server at the IP where you are setting this up.

Installing node

Ghost requires node >= 0.10, the Ubuntu apt repo version (0.6.12 as of writing) is a little too old. The official instructions suggest installing node from source. Before we do that however we'll need to update the server and install some packages:

apt-get update && apt get upgrade -y
apt-get install build-essential sqlite3 unzip

and then we can get on with installing node:

cd /usr/src/
tar xvfz node*
cd node*
./configure —prefix=/usr/local
make install

You should now have node installed. To test try:

node --version

which should report


Don't continue until this works!

Now update the node package manager npm, just in case (oddly, npm does not stand for node package manager):

npm install npm -g

Installing ghost

cd /var
mkdir www
cd www
mkdir ghost
cd ghost
unzip ../
npm install --production

To make ghost start automatically at boot we need to install an init script. We should use upstart, but as it's going away in favour of systemd, and becasue the older sysV scripts still work and ghost includes one lets be lazy and use that instead:

sudo useradd -r ghost -U
wget -o /etc/init.d/ghost
sed -i 's!^DAEMON=.*!DAEMON=/usr/local/bin/node!g' /etc/init.d/ghost
chown -R ghost. /var/www/ghost
chmod 755 /etc/init.d/ghost
update-rc.d ghost defaults

and finally start the service, which by default listens on localhost:2368

service ghost start

Virtual host proxy routing

To publish ghost to the internet on the same IP as other sites we needed a reverse proxy of some kind.

It seems that the node community hasn't yet settled on a solution for virtualhosts with Node. npm lists (npm search vhost) many options of which vhost, vhostd and vhostess seem to be some of the most current. Other suggestions include utilizing the connect framework, for which Jon Simon has an excellent writeup.

I had a quick go with vhostd, which was surprisingly easy to get up and running. A quick sudo npm install vhostd -g (the -g means install with global, machine level scope), followed by creating a /etc/vhostd.ini with the following content

port = 80

address =
port    = 2368

then vhostd start to start the service works like a charm.

I did like this option, but in the end I went for an nginx solution I could add https to more easily (for me), all be it with a self signed certificate for now:

First install nginx

apt-get install nginx -y

and set it to start at boot

update-rc.d nginx defaults

Then create a private key and certificate request. (Note: don't set a password here as you'll have to enter it every time the server starts, and you may not be around to do so)

mkdir /etc/nginx/ssl
openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr

And finally self sign a certificate:

openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

Now create the nginx site file

vi /etc/nginx/sites-available/ghost.conf

with the following content, substituting your domain name for, so for me that was

server {
    listen 80;
    listen 443 ssl;
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;

location / {
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   Host $http_host;
    proxy_set_header   X-Forwarded-Proto $scheme;
    proxy_set_header   X-Real-IP $remote_addr;

and symlink the site to the enabled folder then start nginx

ln -s /etc/nginx/sites-available/ghost.conf /etc/nginx/sites-enabled/ghost.conf
service nginx restart

You should now be able to visit your site at

Setup mail

Ghost needs to send mail. You have several options here, the simplest is probably to setup a gmail account. I needed to send mail from this box anyway so I decided to setup a local mail transport agent (mta). I chose exim becasue it's stable and has a light footprint. There is a great installation guide over at digital ocean.

Configure ghost

Using exim means that ghost keeps pestering you to setup mail unless you put a mail config stanza in the /var/www/ghost/config.js file. This is what my production section looks like

    // ### Production
production: {
    url: '',
    mail: {
             transport: 'sendmail',
             fromaddress: '',
             options: {}
    forceAdminSSL: 'true',
    database: {
        client: 'sqlite3',
        connection: {
            filename: path.join(__dirname, '/content/data/ghost.db')
        debug: false
    server: {
        // Host to be passed to node's `net.Server#listen()`
        host: '',
        // Port to be passed to node's `net.Server#listen()`, 
        // for iisnode set this to `process.env.PORT`
        port: '2368'

Note the addition of the forceAdminSSL: 'true'; directive. This tells ghost to redirect you to HTTPS when you visit the ghost admin page (found at


There you have it, a simple node setup on a server that can also be used to host other sites as well. We even have ghost running under it's own service account which can be locked down to have minimal permissions on the server. :-)