If you are like me, who likes to write, who likes technology and who likes to do this on my own server, then this might be helpful on how to get going with your own blog on your own server and serving it over HTTPS.

As I like new like new technologies and open source I like to use Ghost for any blog installation I make. Not only is it made using node.js and Ember Cli but it’s a blog platform that focuses on the writing which also Medium does. So if you ever feel like moving from Medium to your own self hosted blog, then Ghost could be a good option.

This tutorial is made on a fresh “droplet” on DigitalOcean using the smallest package. The droplet will run CentOS 7.2, I usually choose this one because its one of the more lightweight linux distros available on DigitalOcean. If you want to use any other some of the commands might vary depending on installation you choose. You could also use any other host which gives you console access to your server. I choose DigitalOcean because of its speed, quick setup, price and modern UI, hat off to them!

If you are on DigitalOcean you should have access to your server through SSH and preferably have a domain connected to your droplet.

First step, getting CentOS ready Login to your server through SSH

ssh root@yourdomain.com

And type password when asked for it.

Start by updating your CentOS installation by typing:

yum update

Just to make sure we are up to date! And we’ll install unzip and nginx as it will be needed later.

yum install unzip -y
yum install epel-release -y
yum install nginx -y

The smallest droplet on digitalocean might not have enough memory to install ghost sometimes. Though running it once installed is fine. To solve this we can add some swap space which acts like an extension to your memory but probably not as quick. Type in the following commands to get it up and running. For a more detailed explanation you can check this tutorial.

swapon -s
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

Second step, generate a certificate for your domain

To get an HTTPS connection to your blog and have the little green padlock in chrome we need to get a trusted certificate. This usually costs a lot of money, but a new certificate Authority called Let’s Encrypt makes this really easy and free.

For this we need to install git on the server. To do that, type:

yum install git

Then we’ll clone the Let’s Encrypt repository

git clone https://github.com/letsencrypt/letsencrypt

Next go to the newly downloaded folder and start lets encrypt.

cd letsencrypt
$ ./letsencrypt-auto --help

This triggers an installation of some other things needed by letsencrypt to run on your system.

After this it’s time to generate the appropriate certificates. CentOS is not fully supported yet to be able to use letsncrypt’s client, but we can get certs for a standalone server just fine with the following line:

letsencrypt certonly -d example.com

The above line create a certificate for example.com only. If you also want to create for www.example.com you could also add that domain and any other subdomain you might like to use like this:

./letsencrypt-auto certonly -d example.com -d www.example.com

When prompted, type your email for emergencies and recoveries. Then read their terms and continue if you agree. And…

… taa daa. Your brand new certificate should have been created. A text saying how long your cert is valid will show including the location of your cert which should look something similar to:


Third step, installing Node.js and pm2

One of the easier ways to install node.js is to first add it to the repo of CentOS and then install it from there. Ghost 0.7.8 which is the current version supports node.js versions 0.10.x, 0.12.x & 4.2+. If you prefer one of the older version go ahead and install one of those. Myself I’m a update freak and want to use the latest version I can. To add node.js 4.x to your repo and install type the two following commands.

curl -sL https://rpm.nodesource.com/setup_4.x | bash -
yum -y install nodejs

Once finished you can type node -v to see version number of currently installed version.

There are multiple ways of running ghost once it’s installed. By starting ghost with only node.js or npm ghost will only run for as long as your ssh session is active. What we want is for ghost to keep running after we close our session. To do this there are some handy packages available! I prefer one call pm2. To install pm2 with global access type.

npm install pm2 -g

Npm is a handy package manager that comes bundled with node.js. So once you have node installed npm will also be available.

Fourth step, installing Ghost

Before we install ghost we will create a new user with which will will then run ghost, as using the root user might be a security risk. To create a new user type the following.

useradd ghost
passwd ghost

ghost is the username. By typing useradd ghost you add a user called ghost. But it doesn’t have a password yet and that’s why we write the second line. To add a password for the user ghost. Then when asked type a password for your new user. Once finished we’ll switch to this user account to continue the installation of ghost with the following command.

su ghost

Once you have switched you’ll be in the same folder as you were before which should be the letsencrypt folder in the root user’s domain. Here the user ghost has no permissions to do anything, so we’ll move out of this folder to a folder owned by ghost.

cd /home/ghost

Next we’ll download the latest version of ghost to this folder, unzip it to a folder called blog and then enter that folder with the following commands.

curl -LOk https://ghost.org/zip/ghost-latest.zip
unzip ghost-latest.zip -d blog
cd blog

Now its time to configure ghost to run on the server. To do this we need to edit ghosts config file.

vi config.example.js

This will open the file which will look something like this:

production: {  
    url: ‘http://my-ghost-blog.com',
    mail: {},
    database: {
        client: ‘sqlite3’,
        connection: {
            filename: path.join(__dirname, ‘/content/data/ghost.db’)
        debug: false
    server: {
        host: ‘’,
        port: ‘2368’

We want to change this one so it looks similar to this and matches your server and domain.

production: {
    url: 'http://example.com',
    urlSSL: 'https://example.com',
    forceAdminSSL: true,
    mail: {
        service: 'Gmail',
        fromaddress: 'mymail@gmail.com',
        transport: 'SMTP',
        options: {
            host: 'smtp.gmail.com',
            secureConnection: true,
            port: 465,
            auth: {
                user: 'mymail@gmail.com',
                pass: 'mySecretPassword'
    database: {
        client: 'sqlite3',
        connection: {
            filename: path.join(__dirname, '/content/data/ghost.db')
        debug: false
    server: {
        host: '',
        port: '2368'

To start editing in the vi editor, press i.

When finished editing press ESC and type:


To write and quit the editor. Don’t forget the :

Read more about the editing and setup of the ghost config file you can read about it here.

We’ll copy config.example.js to a new file called config.js which is what ghost will be looking for.

cp config.example.js config.js

Next we’ll install the dependencies needed by ghost to run. For this we’ll again use the npm command.

npm install --production

Fifth step, setting up nginx and firewall

Once npm finishes installing all dependencies we will go back to the root user to setup nginx which is a reverse proxy, to make it use our certificate and point all web traffic to https instead of http. To go back to the root user type exit.


We’ll start by editing a new config file for nginx with the following command.

vi /etc/nginx/conf.d/example.conf

This file does not need to be named example.conf. It can be anything as long as it ends with .conf. I usually name it with my domain name. This file will be empty! We’ll need to add the following

server {
    listen 80;
    listen 443 ssl;
    server_name example.com;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
    if ($scheme = http) {
        return 301 https://$server_name$request_uri;

This configuration will make your server listen to port 80 and port 443 which are the http and https ports. It will serve the certificate. Redirect http users to https and forward users to your ghost blog which runs on port 2368.

ssl_certificate & ssl_certificate_key are the certificates we got from letsencrypt before. Letsencrypt gave us this file in the end /etc/letsencrypt/live/example.com/fullchain.pem. Copy all but the last part which will be replaced with cert.pem and privkey.pem. Once done press ESC again and type:


When saved it’s time to start the reverse proxy.

systemctl start nginx

And check that nginx actually started.

systemctl status nginx

If it doesn’t start you probably wrote something wrong in your config file.

Another good thing to do is to enable nginx to start in case the system reboots.

systemctl enable nginx

Once nginx is running will enable the firewall.

systemctl start firewalld
systemctl status firewalld

Use the last command to check that firewalld is running. Next permissions for http and https needs to be added to the firewall. The reload it to make the changes to take effect.

firewall-cmd --permanent --zone=public --add-service=http
firewall-cmd --permanent --zone=public --add-service=https
firewall-cmd --reload

Almost done! We can now go to the…

Final step, starting ghost

First we need to go back to the ghost user and go to the ghost folder.

su ghost
cd /home/ghost/blog

Moment of truth! If all is done correctly ghost should start just fine with the following command!

npm start — production

If no errors showed up, open your browser and head over to your domain and you should see your ghost blog!

Good! Now we know that everything is working. Then we’ll start ghost with pm2 instead which will keep your ghost installation alive. Press ctrl+c to stop ghost. The start it like

NODE_ENV=production pm2 start index.js --name blog

You’re now done and ready to start blogging!

Go to https://example.com/ghost/setup to finish your setup of ghost.