Setup Ghost Blog in FreeBSD Jail, with single IP

in IT

This tutorial will guide you through setup a FreeBSD jail, install ghost blog inside it, and configure to allow the blog to share the IP address with the main system.
It is written for FreeBSD 10.x, and MAY be adopted for other versions.

It assumes:
1. Your firewall is PF: if you use ipfw, modifications will be needed.
2. Your text editor is vi: if you are not comfortable with vi, subside with something you like. Keep in mind, if using any editor other than vi, you will need to install a copy inside the jail, too. vi(not vim) is the only editor comes with the OS.
3. Assume the ghost blog will share a single IP with the main server.

We will use ezjail for jail management, if ezjail is not yet installed, run:
sudo pkg install ezjail
sudo ezjail-admin install – install base jail
sudo cp /etc/resolv.conf /usr/jails/newjail/etc/ – make sure jails share the same DNS configurations as the main server.
Note: ezjail is the recommonded method because it saves time and get the job done well. However, if you would rather do the jail manually, please read FreeBSD Jails the Hardway in the Extended Reading section at the end of this post.

1. Allow ezjail to run at startup,
& Create a loopback network for hosting the blog:

Run sudo vi /etc/rc.conf to add the following lines to /etc/rc.conf:

#Jail Settings

This creates a lo1 subnet of 8 addresses, start from ( is used to identify the subnet). /29 is the subnet mask, it is the equivalent to

2. Setup jail for ghost

sudo ezjail-admin create blog
sudo ezjail-admin start blog

3. Setup port forwarding with PF:

Run sudo vi /etc/pf.conf to add the following lines to /etc/pf.conf:

this="vtnet0" #change vtnet0 to your network interface if different  

# Allow outgoing connection from jails
nat on $this from lo1:network to any -> ($this)

# Redirect ghost traffic to jail
rdr pass on $this proto tcp from any to $ip_pub port 2368 ->  

Note: rdr pass will immediately pass all traffic on port 2368 to jail, without further firewall filtrations. If firewall filtration desired, delete the keyword passhere, and add corresponding PF rules. How to write such rules is a complicated topic beyond the scope of this tutorial. If you are interested in this topic, please refer to the extended reading section below.

Then run:
pfctl -sn – check if the above rules are set correctly.
sudo service pf reload – load new PF rules.

4. Install ghost

sudo ezjail-admin console blog – switch to the jail console

4.1 Install Dependencies

At the time this tutorial being written(Aug. 2016), the ghost version is 0.9, and it requires npm2, node4, and sqlite3 to run. In the future, be sure to check with Ghost’s official website for updated dependencies requirements.

pkg install node4 – node.js is the runtime environment in which ghost will be running
pkg install npm2 – npm is the package management tool for node.js
pkg install sqlite3 – sqlite is the default database system for ghost
pkg install curl – we will need curl to download ghost software
pkg install sudo – the ghost user will need sudo privilege to run ghost

4.2 Create a ghost user

It is best practice not to run ghost server as the root user.
adduser ghost, then follow promoted instructions to fill up your info.

4.3 Download ghost

We will install ghost in /var/www, you can modify to target a different location if you want.
mkdir -p /var/www
cd /var/www
curl -L --output
unzip -d ghost
chown -R ghost:ghost ghost

4.4 Install ghost

su ghost
cd ghost
npm install sqlite3 --sqlite=/usr/local
npm install --production – this step installs ghost
cp config.example.js config.js
vi config.js, then change the url in line 14 to your url.

4.5 Checkpoint

Now, your ghost blog is ready to run manually, let’s check if all things went right so far:
npm start --production – this starts the ghost service.

Launch a browser that supports javascript(most modern browsers do, Lynx is an example that DOESN’T), and go to http://your_server's_ip_address:2368.

You should be able to see the ghost welcome page as shown below. If not, sometime went wrong, go back and debug.

Then, configure your DNS settings to point your domain to your new Ghost blog.

5. Let Ghost run automatically at system boot

Because Ghost is running on top of node.js, it can not just autostart the same way as native applications. It will require a tool called forever to keep up.
exit – you were in ghost user in last step, exit to root user (of the jail).
npm install forever -g – install forever globally.
vi /usr/local/etc/rc.d/ghost – create startup(rc) script for ghost.
Now paste the following script into this file:

# Copyright (c) 2016 Mo Zhang
# Licensed under the MIT License.


#PROVIDE: ghost
#KEYWORD: shutdown

. /etc/rc.subr

# variables


load_rc_config ghost  
: ${ghost_enable:="NO"}


  sudo -u ghost sh -c "NODE_ENV=production forever start -al $log $ghost/index.js"

  sudo -u ghost sh -c "NODE_ENV=production forever stop $ghost/index.js"


run_rc_command "$1"  

chmod +x /usr/local/etc/rc.d/ghost – make the new script executable
touch /var/www/ghost/ghost.log – create log file
chown -R /var/www/ghost – make sure the log file is owned by ghost user
vi /etc/rc.conf, then add the line:

And last run service ghost start to start ghost.

Also should test the following command:
service ghost stop
service ghost restart

Extended Reading:

Ghost Official Documents:
Jail Official Documents:
FreeBSD Jails the Hard Way:
ezjail Official Documents:
PF Official Documents:
Note: The FreeBSD PF is different than the OpenBSD PF.