My ghost blog is at version 0.7.8 so it's getting a bit outdated with the release of 0.11.3 (and soon the 1.0!). I decided to install it on a Fedora VM and found that there aren't many articles out there.

So here it is: A how-to for installing Ghost on Fedora 25 as a system service and proxied through nginx. If you're more interested in applying my automation efforts on a fresh droplet, I'm working on a script that automates most of the steps in this article.

Prerequisites

This post is specific to Fedora and I'm using DigitalOcean's 1-CPU 512MiB droplet using Fedora 25.

Enable the swapfile for 512MiB VMs

To prevent Ghost's npm install --production call from failing, a swapfile needs to be created. Read the Fedora manual entry for the quick details, or DigialOcean has a detailed post.

Commands to enable a swapfile:

fallocate -l 1G /swapfile
chmod 0600 /swapfile
mkswap /swapfile
swapon /swapfile
echo "/swapfile swap swap sw 0 0" >> /etc/fstab

Verify it's enabled with swapon -s.

Set selinux to permissive

The Ghost service won't start if selinux is enforcing so we want to set it to permissive. I have a TODO on configuring this properly. Stop the enforcement by modifying /etc/selinux/config and setting SELINUX=permissive. Reboot.

Install Ghost

Ghost needs a few OS dependencies then we can use npm to install ghost. However, it's bad practice to use npm with root so we'll create a ghost user and fix permissions on the webroot.

Here's how to do the above as root:

# Install deps
dnf install -y nginx nodejs npm python unzip
mkdir -p /var/www/ghost

# Fetch and extract source
pushd /var/www/ghost
  curl -L https://github.com/TryGhost/Ghost/releases/download/0.11.3/Ghost-0.11.3.zip -o ../ghost.zip
  unzip ../ghost.zip -d .
popd

# Create a system user and fix permissions
useradd --system --create-home --shell /bin/false --user-group ghost
chown -R ghost:ghost /var/www/ghost

# Install Ghost using a non-root account
sudo -H -u ghost /bin/bash -c "cd /var/www/ghost && npm install --production"

If the npm install --production step fails check that the swapfile is enabled and check permissions. Look at the troubleshooting guide for more tips.

Configuration

This part isn't so much fun. There are a few pieces to configure: systemd unit file for a Ghost service; nginx server; nginx to proxy to ghost; ghost.

Ghost systemd unit file

A nice-to-have is Ghost to start on boot and restart itself after a crash. We can do that by creating a systemd unit file.

Create /etc/systemd/system/ghost.service:

[Unit]
Description=ghost
After=network.target

[Service]
Type=simple
WorkingDirectory=/var/www/ghost
User=ghost
Group=ghost
Environment=NODE_ENV=production
ExecStart=/usr/bin/node index.js
Restart=on-failure
SyslogIdentifier=ghost

[Install]
WantedBy=multi-user.target

Now scan for the new unit file, enable ghost on boot, and start it:

systemctl daemon-reload
systemctl start ghost
systemctl enable ghost

Make sure Ghost is running with systemctl status ghost and looking for active (running) in the output. If it failed: double check permissions, selinux policy, and the installation steps.

Nginx server configuration

This how-to assumes a fresh host and the stock configuration has a conflicting default_server. We'll use the below config and not define a server block.

Create /etc/nginx/nginx.conf:

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
include /usr/share/nginx/modules/*.conf;

events {
  worker_connections 1024;
}

http {
  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
  access_log  /var/log/nginx/access.log  main;
  sendfile            on;
  tcp_nopush          on;
  tcp_nodelay         on;
  keepalive_timeout   65;
  types_hash_max_size 2048;
  include             /etc/nginx/mime.types;
  default_type        application/octet-stream;
  include /etc/nginx/conf.d/*.conf;
  gzip on;
  gzip_disable "msie6";
}

NGINX proxy configuration

Our nginx configuration will read configurations from /etc/nginx/conf.d/*.conf, and that's where we'll create the server block to proxy.

Create /etc/nginx/conf.d/mydomain.conf with a simple server block:

server {
  listen 80 default_server;
  server_name mydomain.com;
  client_max_body_size 2G;
  location / {
    proxy_pass http://localhost:2368;
    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_buffering off;
  }
}

Have nginx start on boot and start it:

systemctl enable nginx
systemctl start nginx

Use systemctl status nginx to see if things are working. If so, navigate to http://<yourdomain> and check that ghost is working. It should be :)

Ghost configuration

Ghost has a default configuration that "works" but it's not ideal. Look at /var/www/ghost/config.example.js and read the Ghost manual. Be sure to update the file permissions if any new files are added.

Next Steps

With Ghost installed and working you'll want to change the theme. Probably configure a comment service like Disqus. Email and Domain Keys Identified Mail (DKIM) using AWS. Setup HTTPS using letsencrypt. Maybe add a load balancer if you're really cool.

Let me know if this has worked for you!