My approach is to use only built-ins tools from FreeBSD to run a Ghost instance.

I assume that Node 12 and NPM for Node 12 are installed from the ports collection, and Ghost CLI globally with npm install -g ghost-cli. I don’t cover the Nginx part because it is not FreeBSD specific and already covered elsewhere on the web. Let’s assume that our instance has been created at /usr/local/www/ghost, is owned by the www user, and is functional using the ghost command (start, stop, upgrade, status).

Ghost config modification

First we modify the configuration of our instance so that the logs are displayed on the standard output. The logging section of /usr/local/www/ghost/config.production.json must be something like below so we can forward the logs to daemon.

"logging": {
    "level": "error",
    "transports": ["stdout"]

RC.d script

We could use directly the ghost command in the rc.d script but in my opinion it il a better idea to rely on the daemon(8) program.

#! /bin/sh

# PROVIDE: ghost
# KEYWORD: shutdown

# Add the following lines to /etc/rc.conf to enable `ghost':
# ghost_enable="YES"

. /etc/rc.subr


load_rc_config "${name}"
: ${ghost_enable="NO"}
: ${ghost_home="/usr/local/www/ghost"}
: ${ghost_user="www"}
: ${ghost_group="www"}

long_name="Ghost CMS"

ghost_env="NODE_ENV=production HOME=/nonexistent"

command_args="-S -R 15 -t ${name} -T ${name} -P ${pidfile} -p ${ghost_pidfile} /usr/local/bin/node current/index.js"

ghost_prestart() {
        /bin/mkdir -p /var/run/${name}
        /usr/sbin/chown -R ${ghost_user}:${ghost_group} /var/run/${name}

run_rc_command "$1"

This script is rather simple. There are some subtleties to adapt to the way Ghost manages its PID and also to take into account the slowness of the instance start-up or restart with the ghost command.

command_args="-S -R 15 -t ${name} -T ${name} -P ${pidfile} -p ${ghost_pidfile} /usr/local/bin/node current/index.js"

-S means we are redirecting the output to syslog, -R 15 for waiting 15 seconds for restarting the monitored process after a shutdown, useful at startup but also when using ghost restart or ghost upgrade. -t ${name} is the title of the process (for ps, top, etc.), -T ${name} is the syslog tag, useful for writing the logs in a custom file (we’ll see below), -P ${pidfile} is the PID file of the daemon daemon (lol), -P ${ghost_pidfile} is the PID file of the Ghost instance, this is interesting because we don’t need it but we are writing it at the exact place the ghost command wants it.


I want my Ghost log to be written in /var/log/ghost.log so we are adding a include file, /usr/local/etc/syslog.d/ghost.conf containing the few lines:

*.*                                             /var/log/ghost.log

For the log rotation, a single line in /usr/local/etc/newsyslog.conf.d/ghost.conf:

/var/log/ghost.log                       644  5     1000 @0101T JC

(a copy of the /var/log/messages line). Oh and by the way, we have to filter out the Ghost logs from /var/log/messages by modifying /etc/syslog.conf (see the ghost.none; string):

# diff /etc/syslog.conf~ /etc/syslog.conf
< *.notice;authpriv.none;kern.debug;;mail.crit;news.err /var/log/messages
> *.notice;ghost.none;authpriv.none;kern.debug;;mail.crit;news.err /var/log/messages

service syslog restart to take into account the changes.

Activation and tests

sysrc ghost_enable="YES" to activate the Ghost service, and service ghost start to launch our instance.

The ghost status command should show a running line for our website:

# su -m www
$ cd /usr/local/www/ghost
$ ghost status
│ Name        │ Location             │ Version │ Status               │ URL                 │ Port │ Process Manager │
│ example-org │ /usr/local/www/test  │ 2.31.0  │ stopped              │ n/a                 │ n/a  │ n/a             │
│ example-com │ /usr/local/www/ghost │ 2.30.2  │ running (production) │ │      │ local└─────────────┴──────────────────────┴─────────┴──────────────────────┴─────────────────────┴──────┴─────────────────┘