/ OpenBSD

Deploy Ghost blogging platform under OpenBSD with nginx

Ghost is a simple and fast Node.js blogging system. We'll see here how to deploy it on OpenBSD 6.1 with nginx !

Let's talk about Node :

Node.js is a powerfull JavaScript runtime. It's included in the OpenBSD package system. You can install node and npm (the node's app installer) easily :

# pkg_add node
# node -v
# npm -v

Great ! They are compatible with the latest version of Ghost.
Also, we'll need another package to make it work named : SQLite3.

Install SQLite3 :

# pkg_add sqlite3

Even if you want to use Ghost with MySQL, you'll need SQLite3. The question is... do I need to use MySQL or SQLite3 ?

Short answer : SQLite will be more than fine if you have a blog with less than a 100.000 readers a day.

Setup time :

The Ghost Logo

Get the code :

$ mkdir blog && cd blog
$ curl -LOk https://ghost.org/zip/ghost-latest.zip
$ unzip ghost-latest.zip

Install it as an user :

$ npm install sqlite3 --sqlite=/usr/local
$ npm install --production

Now we can configure it, we'll use the sqlite3 database :

$ vi core/server/config/env/config.production.json

"database": {
    "client": "sqlite3",
    "connection": {
        "filename": "content/data/ghost.db"

$ vi core/server/config/defaults.json

"url": "https://blog.cagedmonster.net",
"server": {
    "host": "",
    "port": 2368

If you prefer using MySQL :

$ vi core/server/config/env/config.production.json

"database": {
    "client": "mysql",
    "connection": {
        "host"     : "",
        "user"     : "ghost",
        "password" : "ghostpass",
        "database" : "ghost"

If you want to add a mail server :

$ vi core/server/config/env/config.production.json

 "mail": {
   "transport": "SMTP"

Start your blog:

We'll use a npm app named forever which allows you to run your node app in the background... forever.

$ npm install forever

Database configuration :

# npm install -g knex-migrator
$ NODE_ENV=production knex-migrator init

Let's start your blog engine :

$ NODE_ENV=production ~/blog/node_modules/forever/bin/forever start index.js

Your app should be running here :

We'll create a crontab task running your app when the system starts :

$ crontab -e
@reboot cd ~/blog/ && NODE_ENV=production ~/blog/node_modules/forever/bin/forever start index.js

Configure nginx with STRONG SSL of course...

# cd /etc/ssl/
# openssl dhparam -out dhparams.pem 4096
# vi /etc/nginx/ssl.conf

ssl_prefer_server_ciphers on;
ssl_certificate /etc/letsencrypt/live/<yourdomain>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<yourdomain>/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_dhparam /etc/ssl/dhparams.pem;
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000;includeSubdomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

# vi /etc/nginx/nginx.conf

server {
   listen 80;
   listen         [::]:80;
   server_name    blog.cagedmonster.net;
   return         301 https://$server_name$request_uri;

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name  blog.cagedmonster.net;
    #ssl_certificate /etc/letsencrypt/live/<yourdomain>/fullchain.pem;
    #ssl_certificate_key /etc/letsencrypt/live/<yourdomain>/privkey.pem;
    include ssl.conf;
                    location / {
                    proxy_set_header Host $host;
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    proxy_set_header X-Forwarded-Proto $scheme;

             location /ghost {
                    proxy_set_header Host $host;
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    proxy_set_header X-Forwarded-Proto $scheme;
                    allow   2a01:cb06:3e0:eb00::/56;
                    deny    all;

# rcctl restart nginx

Now all you have to do is to manage your blog at : https://your-url/ghost/