Running Ghost in a Subdirectory with a Static Nginx Site

When I first installed Ghost to run my blog, one of the drawbacks was the lack of subdirectory support. I wasn't able to set my blog up at, for example.

Fortunately, this has now been fixed (I believe somwhere during v0.4.x, but I'm not certain).

The process for setting this all up isn't completely straightforward, so I thought I'd jot down the process as a reference.

How My Site is Set Up

My current website is hosted on a Ubuntu 14.04 instance by DigitalOcean. Nginx is used to serve my static website and proxy traffic through to my Ghost blog.

I use Git to deploy all updates to the website itself, which is located in /var/www/jonathonstaff-com. Ghost runs from /var/www/ghost.

Previously, I used a pre-built installation of Ghost provided by DigitalOcean, but this time I decided to install everything myself.

Installing Ghost

First, do the basic updating:

sudo apt-get update  

Install Node.js

Because Ghost requires version 0.10.0, we'll add Chris Lea's PPA of Node.js 0.10.33.

sudo add-apt-repository ppa:chris-lea/node.js  
sudo apt-get update  
sudo apt-get install nodejs  

Download Ghost

Download the latest version of Ghost via cURL:

curl -L -o  

Unzip Ghost (you may need to install unzip):

unzip -uo -d ghost  

I personally chose to move Ghost into /var/www to keep consistency on my server:

mv ghost /var/www/  

You'll need to have npm install the packages you need. I only need this to run in a production environment, so I'll pass that flag in also.

npm install --production  

At this stage, you should have a fully functional Ghost blog. We're going to do a bit more configuration to get your static site running.

Configure Ghost

We need to make sure Ghost knows it'll be operating from a subdirectory and not the default url. (You have to do this anyway if you're using Ghost, regardless of running in a subdirectory or not.)

The file we want to edit is called config.js. If you ran npm start after the process above, Ghost will have generated this file for you. Otherwise, simply copy the existing file that's provided and we'll edit it.

cd /var/www/ghost  
cp config.sample.js config.js  
vim config.js  

You really only need to edit the url property in production and development to, but I've provided my full config.js for reference.

var path = require('path'),  

config = {  
    production: {
        url: '',
        mail: {},
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost.db')
            debug: false

        server: {
            host: '',
            port: '2368'

    development: {
        url: '',
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost-dev.db')
            debug: false
        server: {
            host: '',
            port: '2368'
        paths: {
            contentPath: path.join(__dirname, '/content/')

module.exports = config;  

Prepare Nginx and Static Site

To use nginx, we'll first need to install it if it isn't already installed:

sudo apt-get install nginx  

Configure Nginx

There will be a default configuration set up already in /etc/nginx/sites-available/, but feel free to delete it. We'll be writing our own.

vim /etc/nginx/sites-available/default  

If you deleted the default configuration, then Vim should open a blank file here. Just copy and paste the configuration below. Otherwise, you'll have a bunch of other stuff which you can do with as you please. The only part we're concerned with is the following:

server {  
    listen 80;
    access_log /var/log/nginx/ghost.log;

    index index.html index.htm;

    location / {
        root /var/www/jonathonstaff-com;
        try_files $uri.html $uri $uri/ /index.html;

    location ~* /blog {
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   Host      $http_host;

Obviously, you should update the server_name and root accordingly.

So what does each line mean?

  • listen - tells nginx which port to listen on
  • server_name - names the server (change this)
  • access_log - tells nginx where to log
  • index - defines the files to be used as an index
  • location / - defines the configuration for the base url
    • root - tells nginx which directory to serve (this is where we'll put our static site)
    • try_files - describes how non-existent files will be handled
  • location ~* /blog - defines the configuration for files under /blog (i.e. our Ghost instance)
    • proxy_set_header - allows redefining or appending of fields in the request header that are passed to proxied server
    • proxy_pass - sets the protocol and address of a proxied server (note that this should match the server config inside config.js from above)

In order for nginx at actually serve our site, we need to create a symbolic link that says to enable our configuration. With a symbolic link, we can update the configuration in sites-available and it will be updated in sites-enabled as well.

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

Lastly, we want to give public access to the files we're going to serve:

sudo chmod 755 /var/www/  

Deploy Static Site with Git Hooks

Much of this section is borrowed from Abhijit Menon-Sen's article here. I recommend that you read his article if you have any questions.

Remote Server
mkdir jonathonstaff-com.git && cd jonathonstaff-com.git  
git init --bare  
mkdir /var/www/jonathonstaff-com  

What we've done so far: Created a bare repo and the directory that will house our website. Next, we'll add a Git hook to check out the files into a specified directory each time the repo receives a push.

vim hooks/post-receive  

Add the following:

GIT_WORK_TREE=/var/www/jonathonstaff-com git checkout -f  

Make the file executable:

chmod +x hooks/post-receive  
Local Machine

Assuming you already have a git repo set up, all that needs to be done is add a new remote.

git remote add web ssh://  
git push -u web  

As with everything else, this will vary depending on your particular website. If you're running a DigitalOcean instance, it will probably be quite similar. You should add your local machine's ~/.ssh/ to your remote server's ~/.ssh/authorized_keys file if you haven't already.

When the remote server receives the updates you push, it will automatically run the post-receive hook and deploy your site.

Let's Run This

Since we edited the nginx configuration, we need to restart the service if it's not running already. This needs to run as a service so it continues to run even after we kill our session.

sudo service nginx restart  

You should be able to go to your url and see your website being served. All that remains is to start up Ghost, and you're good to go.

I recommend that you install forever so your Ghost blog will automatically restart itself in the case of a failure.

npm install -g forever  
cd /var/www/ghost  
NODE_ENV=production forever start index.js  

Running forever list should show you that your Ghost blog is up and running.

You now simply need to check that everything worked properly by going to Your website should be active at / and your Ghost blog should be accessible in the subdirectory /blog.

If you have any questions / comments, feel free to hit me up on Twitter or drop me a line via email.