I had a little trouble installing Ghost with the SQLite3 backend on FreeBSD. The problem is that there is no precompiled version of node-sqlite3 for FreeBSD and it requires a slight adaptation of the installation process.

First, we have to install Node, NPM & SQLite3. In my example I use the precompiled packages but the port collection gives the same result. All lines beginning with # are from the superuser, and the lines beginning with $ are from the user running Ghost.

# pkg install node12 npm-node12 sqlite3

We have now the handy npm command, plus Python3.7 (As I write this article, my server runs FreeBSD 12.1). As we want to use Ghost with the SQLite backend, we have to install node-sqlite3, but the command # npm install sqlite3 fails:

gyp ERR! find Python Python is not set from command line or npm configuration

Here we could either use npm -g config set python python3.7 or npm install -g --python=/usr/local/bin/python3.7 sqlite3 but (I don’t know why, maybe not everything under the hood take this setup into account) it doesn’t work. I have chosen (not even ashamed) to symlink the python command system-wide with ln -s /usr/local/bin/python3.7 /usr/local/bin/python.

We also have to add --unsafe to get around the following error:

gyp WARN EACCES current user (“nobody”) does not have permission to access the dev dir “/root/.cache/node-gyp/12.18.4”

Finally we have to tell the SQLite module to build from source and to use the natives libraries because there is no binary available for FreeBSD at this time:

# npm install \
    --global \
    --unsafe \
    --build-from-source \
    sqlite3@5.0.0 \
    --sqlite=/usr/local

Now we can install Ghost-CLI globally:

# npm install ghost-cli@latest --global

Then we switch to the user who is going to run Ghost to install our instance.

$ mkdir ghost
$ cd ghost
$ ghost install \
    --url http://127.0.0.1:2468/ \
    --ip 127.0.0.1 \
    --port 2468 \
    --db=sqlite3 \
    --process=local \
    --pname=chach \
    --no-start \
    --no-enable

The important parameters here are:

  • --db=sqlite3 because this is the point of all this mess,
  • --process=local because we are of course no using SystemD,
  • --no-start because we don’t want to start automatically our instance after installation, and
  • --no-enable obviously because of --process=local.

All the parameters can be modified in the config.production.json file at the root of the Ghost website.

$ ghost install --url http://127.0.0.1:2468/ --ip 127.0.0.1 --port 2468 --db=sqlite3 --process=local --pname=chach --no-start --no-enable
✔ Checking system Node.js version
✔ Checking current folder permissions
System checks failed with message: 'Operating system is not Linux'
Some features of Ghost-CLI may not work without additional configuration.
For local installs we recommend using `ghost install local` instead.
? Continue anyway? Yes
System stack check skipped
ℹ Checking operating system compatibility [skipped]
✔ Checking memory availability
✔ Checking for latest Ghost version
✔ Setting up install directory
✔ Downloading and installing Ghost v3.34.0
✔ Finishing install process
✔ Configuring Ghost
✔ Setting up instance
Nginx is not installed. Skipping Nginx setup.
ℹ Setting up Nginx [skipped]
Nginx setup task was skipped, skipping SSL setup
ℹ Setting up SSL [skipped]

Ghost uses direct mail by default. To set up an alternative email method read our docs at https://ghost.org/docs/concepts/config/#mail

------------------------------------------------------------------------------

Ghost was installed successfully! To complete setup of your publication, visit: 

    http://127.0.0.1:2468/ghost/

Nice, right?

Now we can initialize our instance, but with ghost run, not ghost start.

$ ghost run
The `ghost run` command is used by the configured Ghost process manager and for debugging. If you're not running this to debug something, you should run `ghost start` instead.
[2020-09-23 16:15:09] INFO Creating table: posts
[2020-09-23 16:15:09] INFO Creating table: posts_meta
[2020-09-23 16:15:09] INFO Creating table: users
[2020-09-23 16:15:09] INFO Creating table: posts_authors
[2020-09-23 16:15:09] INFO Creating table: roles
[2020-09-23 16:15:09] INFO Creating table: roles_users
[2020-09-23 16:15:09] INFO Creating table: permissions
[2020-09-23 16:15:09] INFO Creating table: permissions_users
[2020-09-23 16:15:09] INFO Creating table: permissions_roles
[2020-09-23 16:15:09] INFO Creating table: permissions_apps
[2020-09-23 16:15:09] INFO Creating table: settings
[2020-09-23 16:15:09] INFO Creating table: tags
[2020-09-23 16:15:09] INFO Creating table: posts_tags
[2020-09-23 16:15:09] INFO Creating table: apps
[2020-09-23 16:15:09] INFO Creating table: app_settings
[2020-09-23 16:15:09] INFO Creating table: app_fields
[2020-09-23 16:15:09] INFO Creating table: invites
[2020-09-23 16:15:09] INFO Creating table: brute
[2020-09-23 16:15:09] INFO Creating table: webhooks
[2020-09-23 16:15:09] INFO Creating table: sessions
[2020-09-23 16:15:09] INFO Creating table: integrations
[2020-09-23 16:15:09] INFO Creating table: api_keys
[2020-09-23 16:15:09] INFO Creating table: mobiledoc_revisions
[2020-09-23 16:15:09] INFO Creating table: members
[2020-09-23 16:15:09] INFO Creating table: labels
[2020-09-23 16:15:09] INFO Creating table: members_labels
[2020-09-23 16:15:09] INFO Creating table: members_stripe_customers
[2020-09-23 16:15:09] INFO Creating table: members_stripe_customers_subscriptions
[2020-09-23 16:15:09] INFO Creating table: actions
[2020-09-23 16:15:09] INFO Creating table: emails
[2020-09-23 16:15:09] INFO Creating table: email_batches
[2020-09-23 16:15:10] INFO Creating table: email_recipients
[2020-09-23 16:15:10] INFO Creating table: tokens
[2020-09-23 16:15:10] INFO Model: Tag
[2020-09-23 16:15:10] INFO Model: Role
[2020-09-23 16:15:10] INFO Model: Permission
[2020-09-23 16:15:10] INFO Model: User
[2020-09-23 16:15:11] INFO Model: Post
[2020-09-23 16:15:11] INFO Model: Integration
[2020-09-23 16:15:11] INFO Relation: Role to Permission
[2020-09-23 16:15:11] INFO Relation: Post to Tag
[2020-09-23 16:15:11] INFO Relation: User to Role
[2020-09-23 16:15:15] INFO Ghost is running in production...
[2020-09-23 16:15:15] INFO Your site is now available on http://127.0.0.1:2468/
[2020-09-23 16:15:15] INFO Ctrl+C to shut down
[2020-09-23 16:15:15] INFO Ghost boot 8.912s

ctrl-c to break and get back to the shell.

ghost start is working now, as well as ghost status.

$ ghost start
✔ Checking current folder permissions
✔ Validating config
✔ Checking memory availability
✔ Checking binary dependencies
✔ Starting Ghost: chach

------------------------------------------------------------------------------

Your admin interface is located at: 

    http://127.0.0.1:2468/ghost/

$ ghost status
┌───────┬─────────────┬─────────┬──────────────────────┬────────────────────────┬──────┬─────────────────┐
│ Name  │ Location    │ Version │ Status               │ URL                    │ Port │ Process Manager │
├───────┼─────────────┼─────────┼──────────────────────┼────────────────────────┼──────┼─────────────────┤
│ chach │ /usr~/ghost │ 3.34.0  │ running (production) │ http://127.0.0.1:2468/ │ 2468 │ local           │
└───────┴─────────────┴─────────┴──────────────────────┴────────────────────────┴──────┴─────────────────┘

We can test that the website is running with:
$ fetch -o - "http://127.0.0.1:2468" | less


I wrote another article about the management of a Ghost instance, the FreeBSD way: Ghost CMS and FreeBSD integration