🍪 Cookie-free

How to run Ghost effectively, from a Raspberry Pi

You will need: Raspberry Pi + Raspbian (HF), a USB stick (recommended), a CloudFlare account (recommended), the Internet, basic SSH skills

Estimated duration: 120 minutes

My little pink Pi, powering this blog

This tutorial is a slimmed (very little explanation, mostly just copiable code), combined version of five previous ones explaining how to run a viable blog on a Raspberry Pi using Ghost. If you want more detail, I recommend you follow the previous five one-by-one (each links to the next at the end).

  1. How to run a Raspberry Pi from a USB drive
  2. How to set up node.js on a Raspberry Pi
  3. How to set up the Ghost blogging platform (with node.js and Forever) on a Raspberry Pi
  4. How to install Varnish Cache on a Raspberry Pi
  5. How to setup CloudFlare CDN on your Raspberry Pi with automatic dynamic DNS updates

Otherwise, crack on.

Here’s an outline of what we’re going to do:

  1. Move the core Raspbian files to a USB stick and run the Raspberry Pi from there.
  2. a. Install node.js. b. Install Ghost. c. Install Forever & have it monitor Ghost. d. Auto-start Ghost & Forever.
  3. a. Install Varnish Cache b. Set Varnish up in front of Ghost, using node.js as the server
  4. Setup CloudFlare

Before I begin, I’d like to thank the following for their work and tutorials, which helped me build this one:

Step One

Migrate Raspbian to a USB stick

#####Full Tutorial

Write a fresh copy of Raspbian to your memory card (download).

Mac/Linux (Terminal)
sudo dd if=path_of_your_image.img of=/dev/rdiskN bs=1m
# where N corresponds to your USB stick


Plug everything except your USB stick in. SSH into it, and configure:

sudo raspi-config

Change the following (don’t expand rootfs):

  • User password (for security, you should always change this).
  • Overclock (use ‘Turbo’ mode, it’s covered by your warranty)
  • Memory split (allocate 16M for GPU)


sudo reboot

When it’s done, plug your USB stick in and run:


Identify your drive (normally ‘sda’), run fdisk on it:

sudo fdisk /dev/sda

Delete existing partitions:


Create a new one:


Write changes:


Format the drive:

sudo mke2fs -t ext4 -L rootfs /dev/sda1

Mount it:

sudo mount /dev/sda1 /mnt

Edit fstab:

sudo nano /etc/fstab

Comment out the default rootfs, add a line for /dev/sda1:

proc           /proc    proc    defaults         0    0
/dev/sda1      /        ext4    defaults,noatime 0    1
/dev/mmcblk0p1 /boot    vfat    defaults         0    2
#/dev/mmcblk0p2 /        ext4    defaults,noatime 0    1
# a swapfile is not a swap partition, so no using swapon|off from here on, use  dphys-swapfile swap[on|off]  for that

Copy everything to USB:

sudo rsync -axv / /mnt

When it’s done, backup cmdline.txt:

sudo cp /boot/cmdline.txt /boot/cmdline.bak

Then edit it to point to /dev/sda and add a 5 second delay:

sudo nano /boot/cmdline.txt

It should look similar to this:

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/sda1 rootfstype=ext4 elevator=deadline rootwait rootdelay=5


sudo reboot

Check everything went okay:

dh -h

The size of your rootfs should reflect your USB stick.

Finally, now that you’re running from the USB stick, you can update:

sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get autoremove

Step Two

Set up node.js, Ghost and Forever

#####Full Tutorial

Install node.js:

cd ~
wget http://node-arm.herokuapp.com/node_latest_armhf.deb
sudo dpkg -i node_latest_armhf.deb

Check it with:

node -v

Install sqlite3 for Ghost:

sudo apt-get install sqlite3

Download, unzip and install Ghost:

cd /var
sudo mkdir www
cd www
sudo curl -L https://ghost.org/zip/ghost-latest.zip -o ghost.zip
sudo unzip -uo ghost.zip -d ghost
sudo rm ghost.zip
cd ghost
sudo npm install --production

When it’s done (about 15 minutes), copy and edit the config file:

sudo cp config.example.js config.js
sudo nano config.js

Change your production URL:

production: {
    url: 'http://my-ghost-blog.com',

Install Forever:

sudo npm install forever -g

Create a startup script:

sudo nano /etc/init.d/ghost.sh

Paste the following into it:

sudo NODE_ENV=production forever start /var/www/ghost/index.js

Make it executable and reboot:

sudo chmod +x /etc/init.d/ghost.sh
sudo update-rc.d ghost.sh defaults
sudo reboot

After reboot, check it’s running before continuing:

sudo forever list

Step Three

Set up Varnish

#####Full Tutorial

Install prerequisites:

sudo apt-get install autotools-dev autoconf libpcre3-dev libedit-dev libncurses5-dev automake libtool groff-base python-docutils pkg-config -y

Clone, make and isntall Varnish Cache:

cd ~
git clone git://git.varnish-cache.org/varnish-cache
cd varnish-cache
sh autogen.sh
sh configure --enable-diagnostics --enable-debugging-symbols

This’ll take a while. When it’s done, install, create its user, add a default configuration:

sudo make install
sudo ldconfig -n /usr/local/lib/
sudo useradd varnishd
cd /usr/local/etc
sudo mkdir varnish
cd varnish
sudo nano default.vcl

Put the following into it:

vcl 4.0;
backend default {
.host = "";
.port = "2368";

sub vcl_recv {
if (req.http.cache-control ~ "no-cache") {
set req.hash_always_miss = true;

set req.http.x-pass = "false";
if (req.url ~ "^/(api|signout)") {
set req.http.x-pass = "true";
} elseif (req.url ~ "^/ghost" && (req.url !~ "^/ghost/(img|css|fonts)")) {
set req.http.x-pass = "true";

if (req.http.x-pass == "true") {
unset req.http.cookie;

Create a Varnish startup script:

cd /var/www
sudo nano start-varnish.sh

Paste the following into it:

ulimit -n 10240
ulimit -l 16384
/usr/local/sbin/varnishd -f /usr/local/etc/varnish/default.vcl -a -s malloc,16M -l 8m,1m,+ -u varnishd

Make it executable:

sudo chmod +x start-varnish.sh

Reopen your Ghost startup script:

sudo nano /etc/init.d/ghost.sh

Add the line:

sudo sh /var/www/start-varnish.sh

Update rc.d:

sudo update-rc.d ghost.sh defaults

Now we need to enable caching in Ghost (it’s turned off by default) for Varnish to work:

sudo nano /var/www/ghost/core/server/middleware/middleware.js

Change max-age here to whatever you want in seconds:

// ### CacheControl Middleware
// provide sensible cache control headers
cacheControl: function (options) {
    /*jslint unparam:true*/
    var profiles = {
            'public': 'public, max-age=XXX',


sudo reboot

Step Four

Set up CloudFlare

#####Full Tutorial

Create an update script:

cd ~
nano cfupdate.sh

Add the following (edited for your account):


# CloudFlare-registered email address
[email protected]

# API key

# The first nameserver CloudFlare gave you

# The URL you want to update the IP address for,
# you can use a comma-separated list for multiple

# Get our current external IP address
CFIP=$(curl -s http://myip.dnsomatic.com/)

# Build the URL you need to do the update

# Check current CloudFlare-listed IP address
CFHOSTIP=$(nslookup $(echo $CFHOSTS | cut -d ',' -f1)$CFNS | grep Address | tail -1 | cut -d ' ' -f2)

# If IPs differ, update CloudFlare
if [ "$CFIP" != "$CFHOSTIP" ]
    /usr/bin/curl -s $CFURL

Make it executable:

sudo chmod +x cfupdate.sh
sh cfupdate.sh

Set it up on a cron job:

sudo crontab -e

Give it a 1 minute interval (more if you want):

*/1 * * * * sh ~/cfupdate.sh


sudo reboot

Check everything

At this point, everything should be set up. If your cron job’s run, CloudFlare should be pointing your domain’s DNS record to your Pi.

Head over to your blog’s URL to check it’s running, and to make sure Varnish is running you can visit:


Any problems? Drop me a line in the comments.

Feel free to share this post, or reproduce it or parts of it (I’d appreciate a link back if you do).