Deploy Ghost on Debian with nginx and PM2
With the release of Ghost 1.0, this guide is now deprecated. Please check out the official documentation on how to install the latest version.
This is a step by step guide on how to deploy Ghost on Debian (Jessie) using nginx as a reverse-proxy, and PM2 as our Node.js process manager. This guide is part of the Deploy Ghost series and is split into three parts - Deploying, Securing, and Optimising. This is part one and once we’re done, we will have a fully functional Ghost blog (running over HTTP).
Before you start typing into your console, please take the time to research and understand what it is that you’re actually executing on your server. This process worked for me, and might change as the applications have new patches and versions rolled out.
Series Links #
If you want to have your own Ghost server, but don’t want to manage it, the Ghost Foundation offer a hosted solution. If you want to get your hands dirty and help out, they’re happy for anyone to contribute.
Get Utilities #
I’m assuming we’re running as root for the time being. Once we create another user, that’s when we’ll start executing commands as them. We’ll start by getting some utility software that we can use later on.
apt-get update
apt-get install curl
apt-get install zip
Install and Prepare nginx #
The Debian nginx repository tends to lag behind in release versions. At the time of writing, it has version 1.6.2, which is missing some security features we want later on. To get the latest version, we need to add in a new source for nginx and get their GPG key.
So log in as root and let’s edit our sources list.
nano /etc/apt/sources.list
We want to add the below to the bottom of the sources.list file.
# nginx
deb http://nginx.org/packages/mainline/debian/ jessie nginx
deb-src http://nginx.org/packages/mainline/debian/ jessie nginx
Now we need to grab the GPG key and add it. Once it’s been added, we can delete the key.
curl -O https://nginx.org/keys/nginx_signing.key && apt-key add ./nginx_signing.key
rm nginx_signing.key
Update our repositories so we can download and install nginx.
apt-get update
apt-get install nginx
We need to setup nginx so that we can easily administer it later. To do that, we’ll make /var/www/
our webroot. We will also use /etc/nginx/sites-available
to store our individual configuration files, and create a symlink to /etc/nginx/sites-enabled
when we’re reading for them to go live.
cd /var
mkdir www
cd /etc/nginx
mkdir sites-available
mkdir sites-enabled
In order for nginx to know about our sites-enabled
configuration files, we have to tell it to include any configuration files it finds in there. We make this change in the nginx.conf
file.
nano nginx.conf
Add the below string in to the end just after include /etc/nginx/conf.d/*.conf;
and save the file.
include /etc/nginx/sites-enabled/*;
Now we can remove the default configuration file.
cd conf.d
rm default.conf
Add a user #
We want a dedicated user to run Ghost and the associated processes, so let’s go ahead and create a user called ghost
and give it sudo
access.
adduser ghost
adduser ghost sudo
Log out of root
and log into ghost
. Going forward we’ll be working from the user ghost
.
Install Node.js #
We want to install Node.js with NVM so we don’t need to execute things as root (or with sudo), it also makes managing Node.js that much easier. We’ll download and install it. This command will get NVM version 0.32.1, there might be a new one, so check over here.
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.32.1/install.sh | bash
In order for NVM to take effect, we need to log off and log in again. If you don’t, you will get an error.
As of writing, Ghost supports Node V4 so we can use the Argon LTS to get the latest in V4. We then want to make it our default Node version.
nvm install --lts=argon
nvm alias default node
Once it’s installed, we can verify the Node and NPM versions with node -v
and npm -v
.
Install Ghost #
To install Ghost we want to get it and extract it to it’s own directory.
curl -L https://ghost.org/zip/ghost-latest.zip -o ghost.zip
mkdir ghost
unzip ghost.zip -d /home/ghost/ghost
cd ghost
Now we need to execute Ghost so it can perform the first run setup.
npm install --production
npm start --production
When we execute npm start --production
, we will need to press ctrl
+ c
to get our console control back. This will shutdown our blog, which is good for now.
Now we need to move our Ghost directory somewhere that nginx can deal with. Since we’re using /var/www/
as our web root, we’ll move it there. We’re done with ghost.zip
so we can delete it too.
cd ../
sudo mv /home/ghost/ghost/ /var/www/
rm ghost.zip
Configure Ghost #
A simple change to update our URL and Mail settings inside Ghost is needed. We’ll need to change directory to our Ghost install and alter the url
and mail
settings inside config.js
.
cd /var/www/ghost
nano config.js
We want to change url: 'http://my-ghost-blog.com',
to whatever your URL is. I will use example.com. The mail settings should also be changed - there’s a few ways to do it so read the
Ghost mail configuration page.
Below is a small extract of what config.js
will look like once we’re done.
config = {
// ### Production
// When running Ghost in the wild, use the production environment.
// Configure your URL and mail settings here
production: {
url: 'http://example.com',
mail: {
transport: `SMTP`,
options: {
service: `Gmail`,
auth: {
user: '[email protected]',
pass: 'yourpassword'
}
}
},
database: {
// More content.
},
},
// More content.
}
Configure ‘ghost’ ngnix File #
We can create our Ghost configuration file in sites-available
, this will be used to let nginx know about Ghost. We will make any nginx changes that relate to Ghost in this file. We’ll call the configuration file ghost.conf
.
cd /etc/nginx/
sudo touch /etc/nginx/sites-available/ghost.conf
sudo nano /etc/nginx/sites-available/ghost.conf
Inside our freshly created configuration file, we want to add in the below content and save it.
server {
listen 80 default_server;
root /var/www/ghost/;
server_name example.com www.example.com;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://127.0.0.1:2368;
}
}
Now we want to create a symlink to enable Ghost inside nginx.
sudo ln -s /etc/nginx/sites-available/ghost.conf /etc/nginx/sites-enabled/ghost.conf
In order for our changes to take effect, we need to parse our file to check for errors, and if it’s good then we want to reload nginx.
sudo nginx -t && sudo nginx -s reload
If you try and hit your blog now, you’ll be provided with a 502 Bad Gateway error. Our Ghost blog isn’t actually running just yet, so there’s nothing there.
Keep Ghost running with PM2 #
We’ll use PM2 to make sure Ghost starts up again if the server restarts. PM2 also makes it easier to manage the Ghost process and let’s us easily manage other Node processes.
cd /var/www/ghost
npm install pm2 -g
NODE_ENV=production pm2 start index.js --name="GhostBlog"
pm2 save
pm2 startup debian
After we execute pm2 startup debian
, we’ll get an output saying you need to execute another command. You’ll need to type it in exactly as it says and execute it otherwise Ghost won’t start when the server restarts.
Ghost is Deployed #
We’ve now got a functioning blog, but it’s seriously insecure. If you log in to the Ghost admin panel, those credentials are sent in plain text. That’s really bad. Part two, Securing Ghost will cover that particular issue.