Running Ghost on an Entry Level Digital Ocean Debian VPS

I’m a fan of the $5/mo droplet that Digital Ocean offer. For that price, you get a VPS in the cloud with 512MB RAM, a single core CPU (2.4GHz Xeon), 20GB of SSD storage and 1TB of bandwidth – perfect for testing things and small side projects. I decided to try Ghost, which looks like an ideal platform to play with.

Digital Ocean also offer one-click apps, which are pre-built VMs you can spin up in seconds with everything ready to go. Ghost is one of those options, but unfortunately the smallest size droplet it can be installed on is the $10/mo option. Whilst this doesn’t break the bank, Ghost is definitely capable of running on the smaller VM.

It’s always good to build your own droplets now and then instead of relying on the one-click apps, you’ll learn a lot about what your server is doing and how to customise things to your liking.

$5 droplet: The only thing we’ll be doing differently is adding SWAP, apart from that, the following steps will install Ghost on any Debian VPS from any provider.

Pre-requisites

We’ll be setting up a simple Ghost blog, on a $5 droplet, using a MySQL backend and nginx as the front door.

This article is up-to-date as of August 2017, using the following software and versions:

Debian 9.1

nginx/1.10.3

ghost/1.0.0-rc.1

mariadb/10.1.25

You won’t need much Linux knowledge, but you should at least know how to traverse around the filesystem, as well as understand basic permissions and user/group settings.

You’ll need to have a domain setup with the appropriate record setup pointing to your droplet. Also note, I choose to setup a www domain, and I forward non-www to www in DNS, rather than with nginx – do whatever suits you.

2. Linux Housekeeping

I’ve removed some of the irrelevant motd message above, but you may notice your packages are already overdue an update, as packages will have been updated since Digital Ocean last refreshed their Debian image. Run the following commands to get us up-to-date:

We can now disable remote root login to SSH, and only allow logins with keys. Open up the sshd config file with sudo vim /etc/ssh/sshd_config and change the values to the following:

PermitRootLogin no
PasswordAuthentication no

Save the file and restart the SSH daemon.

ghost@ghost1-nyc3:~$ sudo service sshd restart

We now have a VPS with a non-root user that we can login as, and use sudo to execute root commands. We’ve also locked down SSH, so keys are required – nice and secure.

3. Add SWAP Memory

One of the reasons getting Ghost going on a smaller VPS can be an issue is that the installer will fail if there’s not enough memory available – and 512mb isn’t enough! Ghost may be able to work fine on these specs, but installs and upgrades won’t work.

To get around this, we can use SWAP memory. For the uninformed: SWAP allows us to allocate disk space to the operating system for use as RAM. On servers with regular HDDs this can cause performance issues, but as DO only offers SSDs the impact is lessened.

We’re going to add a 1GB SWAP file, which should be plenty for upgrades and emergencies.

All is well, but if we reboot, we’ll lose our SWAP. In order to make the changes permanent, we’ll need to add the changes to our /etc/fstab file. Open the file using vim and add the file to the end, it should look something like this:

LABEL=cloudimg-rootfs / ext4 defaults 0 0
/swapfile none swap sw 0 0

Note: The spacing isn’t important, but it’s nice to keep things neat

Swappiness

There’s some additional tweaks we can make to SWAP for better performance. The exact numbers you can use will vary from situation to situation, and people have their own opinions about what values to use.

vm.swappiness – a numer between 0-100 that dictates how ‘swappy’ Linux acts. Setting this to 20 means that Linux will begin to SWAP when memory usage hits 80%. Setting this value to 0 would disable SWAP, whilst 100 would always utilise it. We will set it to 10.

Setting this variable is similar to SWAP – we run a command to make the change immediately, then edit a config file to make the change persist over reboots.

Open your sysctl file with vim by typing sudo vim /etc/sysctl and add the following value:

vm.swappiness=10

4. Install Required Software

nginx

In order to accept connections to our server, deal with SSL and more, we’ll need nginx. Ghost will listen privately on port 2368, nginx will listen publicly on 80 and 443 (http and https) and proxy requests to Ghost.

ghost@ghost1-nyc3:~$ sudo apt-get install nginx

MariaDB (MySQL)

For those out of the loop, Oracle acquired MySQL. Because of this, some of the original developers forked MySQL and created MariaDB – a community maintained version intended to remain free forever.

There’s additional security steps you can optionally take here. For example, if you have a static IP for your connection or VPN, you can lock down SSH to only allow from that address. You can also run SSH on a non-standard port which is common good practice, feel free to adjust as you wish.

6. Take a Snapshot

We’re almost ready to configure Ghost. The server has our user accounts, software and config all ready to go. At this point, it’s often a good idea to take a snapshot in the Digital Ocean control panel and save it as something like ‘ghost-base’. This means any time you need to setup a Ghost droplet, you can spin up your own one-click app.

7. Pre-configure nginx

I’ve ran into issues using the CLI Ghost installer when trying to get an SSL certificate and nginx setup, so I choose to do the first bit manually and then run the installer. The installer will repeat the LetsEncrypt steps later but it doesn’t matter, I’ve found it sometimes doesn’t work unless you do this step, but you might be fine skipping ahead to #8.

We’ve now got our config in place and enabled it, we just need to reload nginx for the changes to take effect. It’s a good habit to always verify your nginx config with nginx -t before you try and reload/restart the service.

We’ve created a temporary server using nginx, that’s listening on port 80 and will allow access a folder called .well-known – used by LetsEncrypt for initial verification. Let’s create the relevant folders:

8. Add SSL to nginx

We now use certbot, specifying the webroot folder we created earlier and the domain we’re using:

ghost@ghost1-nyc3:~ $ sudo certbot certonly --webroot --webroot-path=/var/www/ghost -d <YOUR SITE>
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for <YOUR SITE>
Using the webroot path /var/www/ghost for all unmatched domains.
Waiting for verification...
Cleaning up challenges
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/<YOUR SITE>/fullchain.pem. Your cert
will expire on <DATE>. To obtain a new or tweaked version of
this certificate in the future, simply run certbot again. To
non-interactively renew *all* of your certificates, run "certbot
renew"
- If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
ghost@ghost1-nyc3:~ $

We now have certificates installed and we can get on with installing Ghost using the CLI installer.

9. Install Ghost

We’ll need to create a directory for it to live in, and allocate the correct owner. We’ll also need to erase the nginx config we made earlier or the tool won’t write config out.

During the installer: use your https domain as the blog URL, allow setup of nginx and SSL, and you can answer n to mysql user creation as we’ve already done that step.

502 Bad Gateway

If you instead are greeted by an nginx 502 Bad Gateway error, you may have a port mismatch. For some reason, sometimes when I’ve installed Ghost, it listens on a different port to the one nginx is configured to foward to. We can check this with netstat -plotn:

We can see that nginx is listening on tcp/80 tcp/443 on both IPv4 and IPv6, MySQL is listening locally on its native port of tcp/3306, and ghost is listening on tcp/2368. Let’s check that against what we have configured in nginx:

Wrap-up

Past this point, I’d recommend downloading the Ghost desktop app and tying it to your new blog, it’s pretty seamless and is a breath of fresh air if you’re coming from WordPress.

Multi-Tenancy

We setup a single tenancy server. It’s easily possible to host multiple Ghost sites on a server. You’ll need to create new MySQL details and /var/www/<name> directory, you should be able to accomplish most of the rest with certbot and ghost-cli, specifying a new domain/directory where required, using the commands in this article.

You will need to ensure your local Ghost installs are listening on different ports (usually tcp/2369 onwards), and the nginx config reflects this.