7 years, 18 days ago

Running Ghost on FreeBSD 9.1 with MariaDB and nginx

I have recently spent some time on configuring Ghost on my FreeBSD server. For various technical reasons the default Ghost deployment model running on sqlite3 does not work on FreeBSD (I get some mysterious build errors when running npm install --production on my box), so I had to find another database to run it on. Luckily Ghost runs on MySQL/MariaDB as well so it was just a matter of changing a few lines of configuration.
After setting up the MariaDB configuration and starting Ghost, I then put nginx in front of the Node http server with a simple upstream http proxy so that I can host other web sites on the same box. nginx is extremely easy to configure (particularly compared to the Apache 2.2 configuration file format) and blazingly fast.

Preparing Your Database

This guide assumes that you already have MariaDB/MySQL running on the same box (localhost). If not, please install it from Ports first. Open mysql and run the following SQL:

create database ghost_dev; 
create database ghost_prod; 
create user 'ghost'@'localhost' identified by 'secret'; 
create user 'ghost'@'<hostname>' identified by 'secret'; 
grant all privileges on ghost_dev.* to 'ghost'@'localhost' with grant option;
grant all privileges on ghost_dev.* to 'ghost'@'<hostname>' with grant option;
grant all privileges on ghost_prod.* to 'ghost'@'localhost' with grant option;
grant all privileges on ghost_prod.* to 'ghost'@'<hostname>' with grant option;
flush privileges;

The above snippet creates two databases, ghost_dev for development and testing and ghost_prod for production. Depending on your MySQL configuration it is necessary to create the ghost user for both localhost and ‘hostname’ (e.g. jensenwaud.com) for the Node MySQL driver, since, for some reason, it will now allow us to connect using ‘ghost@localhost’ as the username.

Installing Node

First you need to install Node from Ports. As root run portsnap fetch update to get the latest version. I am running version 0.10.25 as we speak.

cd /usr/ports/www/node
sudo make install

Installing Ghost

Download and unzip the Ghost zip archive from the Ghost web site. I downloaded 0.4.1. Tip: If you do not have unzip installed on your box, you can install it from /usr/ports/archivers/unzip.
Before you extract the zip file, move the archive into a sub folder on its own, e.g. /home/aj/ghost, otherwise it will extract into your current sub directory!

mkdir -p /home/aj/ghost
mv ghost-0.4.1.zip /home/aj/ghost && cd ghost
unzip ghost-0.4.1.zip

After unpacking, run the following commands to install all npm package dependencies including the additional MySQL/MariaDB driver (remember we want to avoid using sqlite3!):

npm install mysql
npm install --production

If you are on FreeBSD 9.1, supposedly, the last command should fail when trying to install the sqlite3 package. Don’t worry, we will change it to MySQL/MariaDB in a minute prior to deployment.
Ghost is now installed, the next step is to install and configure the database driver in ghost/config.js (do cp config.example.js config.js first before editing the file). Under the database: section remove the existing section and add the following, both for development and production:

    database: {
        client: 'mysql',
        connection: {
          host: 'localhost',
          user: '<user name>',
          password: '<password>',
          database: '<database name>',
          charset: 'utf8'
        },

This assumes that you have already configured a database on your MariaDB/MySQL instance. If not, please consult the manual on how to do so. Note: I use two different databases for development and production, i.e. ghostdev and ghostprod. Before you save the file, don’t forget to set the correct host name under the url parameter. It must match the same settings that you put into nginx later on.

My full settings for production deployment now look like this:

// ### Production
// When running Ghost in the wild, use the production environment
// Configure your URL and mail settings here
production: {
    url: 'http://blog.jensenwaud.com',
    mail: {},
    database: {
        client: 'mysql',
        connection: {
          host: 'localhost',
          user: 'ghost',
          password: 'secret',
          database: 'ghost_prod',
          charset: 'utf8'
        },
        debug: false
    },
    server: {
        // Host to be passed to node's `net.Server#listen()`
        host: '127.0.0.1',
        // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
        port: '2368'
    }
},

Installing and Configuring nginx

The next step is to install nginx from Ports (as root):

cd /usr/local/www/nginx
make install

No special settings are required, unless, of course, you have special considerations for other web sites you may be deploying. The version I installed at the time of writing this blog entry was nginx version 1.4.4.
After nginx has installed, open /usr/local/etc/nginx and paste the following configuration into nginx.conf:

user nginx;
worker_processes 4;
pid log/nginx.pid;

events {
    worker_connections 768;
    # multi_accept on;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    # server_tokens off;
    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /usr/local/etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # Logging Settings
    ##

    access_log log/access.log;
    error_log log/error.log;

    ##
    # Gzip Settings
    ##

    gzip on;
    gzip_disable "msie6";

    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/      xml application/xml+rss t

    ##
    # Virtual Host Configs
    ##

    include /usr/local/etc/nginx/conf.d/*.conf;
    include /usr/local/etc/nginx/sites-enabled/*;
}

The line at the top designates the UNIX user, which nginx will impersonate – so we need to create it. Create a standard UNIX account with /usr/sbin/nologin as shell and a secure password:

adduser nginx

After creating the user, create a new directory for nginx to place its log files and pid:

cd /usr/local/etc/nginx && mkdir log
chown -R nginx log    

Log files and the Ghost pid are now written to /usr/local/nginx/log. Create a new directory to hold all virtual host configuration files (only one for Ghost for now):

mkdir -p /usr/local/etc/nginx/sites-enabled

With the following content:

server {
  listen         80;
  server_name blog.jensenwaud.com;
  root /home/aj/ghost/;
  index index.php;

  if ($http_host != "blog.jensenwaud.com") {
       rewrite ^ http://blog.jensenwaud.com/$request_uri permanent;
  }

  location / {
      proxy_set_header X-Real-IP  $remote_addr;
      proxy_set_header X-Forwarded-For $remote_addr;
      proxy_set_header Host $host;
      proxy_pass http://127.0.0.1:2368;
  }

  location ~* \.(?:ico|css|js|gif|jpe?g|png|ttf|woff)$ {
      access_log off;
      expires 30d;
      add_header Pragma public;
      add_header Cache-Control "public, mustrevalidate, proxy-revalidate";
      proxy_pass http://127.0.0.1:2368;
  }

  location = /robots.txt { access_log off; log_not_found off; }
  location = /favicon.ico { access_log off; log_not_found off; }

  location ~ /\.ht {
          deny all;
  }
}

Enable nginx on boot and as a service in /etc/rc.conf:

nginx_enable="YES"

Switch to the ghost directory and start the server:

cd /home/aj/ghost
npm start --production &

Start nginx as a daemon (as root):

/usr/local/etc/rc.d/nginx start

Finally, open your browser and point it to the URL for the virtual host:

firefox http://blog.jensenwaud.com/ghost

And you are done!