Deploy Ghost blogging system using Passenger on Dreamhost or other hosting providers

; Date: Mon Mar 05 2018 16:00:00 GMT-0800 (Pacific Standard Time)

Tags: Node.js

Ghost is a very popular blogging platform which, because it's written in Node.js, is not what you'd expect to install on a traditional web hosting provider who supports PHP. Dreamhost is one of the many dozens or hundreds of web hosting providers whose bread-and-butter is PHP application hosting. Dreamhost, for example, has a specially configured Wordpress hosting service, Dreampress, that looks very useful. However, Dreamhost also allows hosting of Node.js applications. Let's see how to get Ghost running on a Dreamhost Virtual Private Server.

For general directions on running Node.js apps on Dreamhost: Deploy Node.js application using Passenger on Dreamhost or other hosting providers

Bottom line is these observations:

  • Dreamhost uses a thingymajob called Passenger that was meant for Ruby and Python, but also supports Node.js apps -- getting that to support Ghost has a little trick to it, but is not hard
  • Dreamhost prohibits Node.js installation on a shared hosting account, it can only be done on a VPS
  • The Ghost installer expects it's being run as root in a VPS and that it must do some things to set up nginx for proxying, and to create a low-privilege user to run Ghost -- but Dreamhost VPS's don't support the necessary access -- so we have to run this a little differently
  • While the Ghost team warns against using Node.js installed by nvm that's what we must do

Installing nvm

We need a fairly up-to-date version of Node.js, and the crufty old version Dreamhost supplies in the crufty old Ubuntu 14.04 we're provided on VPS's just doesn't cut it. Fortunately nvm makes it easy to install any Node.js version you wish. Getting Ghost to use it is just a matter of a little configuration.

Instructions: (

In a terminal session on your Dreamhost VPS, type this command:

$ wget -qO- | bash

Ghost supports Node.js 4.5, 6.9 and 8.9, so type this:

$ nvm install 8
v8.9.4 is already installed.
Now using node v8.9.4 (npm v5.6.0)

Installing Ghost-CLI

With Node.js installed type:

$ npm install -g ghost-cli

This installs the Ghost command-line tool, documentation: (

Setup subdomain w/ Passenger/Node.js support

There's more instructions on this at: Deploy Node.js application using Passenger on Dreamhost or other hosting providers

In the Dreamhost control panel, set up a new domain (or subdomain), with these entries in the form:

  • Type a domain to use - like
  • Click either Add WWW or Remove WWW depending on your preference
  • For the user account to run this under, choose one you have on your VPS -- I have one VPS with one user ID installed, and chose that user
  • For Web Directory, you'll be presented with /home/username and in the text entry box the domain (e.g. you'll use. You MUST extend the text in the box to read DOMAIN.TLD/public or e.g.
  • Click the button for Passenger
  • In the buttons that show themselves, select Node.js

Then click the button at the bottom to setup the domain.

When the Dreamhost installation software sets up the DOMAIN.TLD directory, delete all contents. The Ghost installer will complain about being run in a non-empty directory and refuse to do anything.

Install and setup Ghost

The Ghost team has setup instructions which we'll use parts of: (

The steps taken so far are preparatory so we can do the following.

Ideally you just run this and it would install:

$ ghost install --db=sqlite3

Ghost can also run against MySQL, and Dreamhost offers a MySQL service. I wanted to instead use SQLite for now, and setup the MySQL service later.

Unfortunately that produces this error message:

1) SystemError

Message: The path /home/USERNAME/ is not readable by other users on the system.
This can cause issues with the CLI, please either make this directory readable by others or install node and Ghost-CLI in another location.

The fix is this:

$ chmod 755 $HOME

Dreamhost probably set this up with a 700 permissions which is a useful permission setting for shared hosting. However, we're on a VPS and there's noone else on that VPS and therefore making the directory readable by others is perfectly fine.

Rerunning the above command still fails because Ghost wants to do this:

Running sudo command: useradd --system --user-group ghost
? Password [hidden]
? Password [hidden]
? Password [hidden]
✖ Setting up "ghost" system user

Remember when I said at the top the Ghost installer is assuming it's run by root on an empty VPS and it must set up a whole bunch of things including a low-privelege user. There's certain things we must instruct the Ghost installer to skip over.

See: (

That page covers the ghost setup subcommand, which says there are several stages to the ghost install command. For each stage it calls ghost setup to perform the stage. If we want the installer to skip a stage, we use --no-setup-STAGE-NAME. In this case:

$ ghost install --db=sqlite3 --no-setup-linux-user --no-setup-nginx --no-setup-ssl --no-setup-systemd

For the last three of those you can simply answer No when it asks, like so:

? Do you wish to set up Nginx? No
ℹ Setting up Nginx [skipped]
Task ssl depends on the 'nginx' stage, which was skipped.
ℹ Setting up SSL [skipped]
? Do you wish to set up Systemd? No
ℹ Setting up Systemd [skipped]
✔ Running database migrations
? Do you want to start Ghost? No

We end up with a directory containing:

$ ls
config.production.json	content  current  versions

We're not ready to run anything yet, and have three things to do.

First, we need to setup the public directory we deleted a ways back. We needed the directory to be empty for the Ghost installer to even run, but the Dreamhost Passenger setup requires the public directory and more specifically a .htaccess in that file.

$ mkdir public
$ touch public/.htaccess

Then in .htaccess add this line:

PassengerNodejs /home/USERNAME/.nvm/versions/node/v8.9.4/bin/node

Adjust this for the Node.js version you have installed.

Finally we need a file named app.js to start the application. Passenger looks for this file, and while the Passenger documentation seems to support a way to change which file it looks for I couldn't figure it out. Instead I added a file, app.js, containing this:

const app = require('./current/index.js');

The file current/index.js is the module used to launch Ghost. This takes care of loading that module and executing its code.

A configuration file is in the directory as well, and you can use something like this:

  "url": "",
  "server": {
    "port": 80,
    "host": ""
  "database": {
    "client": "sqlite3",
    "connection": {
      "filename": "./content/data/ghost.db"
  "mail": {
    "transport": "Direct"
  "logging": {
    "transports": [
  "process": "systemd",
  "paths": {
    "contentPath": "/home/USERNAME/"

Running and troubleshooting

Once all that is running, just visiting http://DOMAIN should bring up Ghost, and you'll be good to go.

If something goes wrong, the error log may have clues in $HOME/logs/DOMAIN/error.log