It seems I finally decided to start a blog after all this time merely thinking about it. I hope I'll be able to make it my online memory and maintain it until I retire some day.

To start my blog off, I thought I'd make a post about my current setup. I registered my domain with Namecheap, and rented from DigitalOcean the smallest droplet (aka VPS) available. I chose to put Ubuntu Server on it; I heard it is what people recommend to Linux newcomers.

The blogging application I'm using is called Ghost. I'm thinking I could've gone with a static website generator instead, but I wanted to try out a Node.js application. So far it fits the bill.

The rest of this post details the steps I went through to setup Ghost on my VPS. I'm a complete Linux newbie, so I detailed some basic Linux-y stuff for my own sake.

Create an admin user

Using the root user is not recommended, but I still need a user that can run administrative tasks. I'll create one here.

# 1. Create the user admin.
# 2. Add admin to the sudo group. 

root$ adduser admin  
root$ gpasswd -a admin sudo  

Setup SSH

When creating a Linux droplet, I could choose to enable SSH login for the root user, which I did. Now that I have a new admin user, I will enable SSH login for it and disable SSH login for root.

# Enable SSH login for admin
# 1. Switch to user admin. The "-" means switch to user's dir and set env.
# 2. Disable the sudo hint for admin [optional].
# 3. Create the user's ssh directory.
# 4. Add an authorized ssh key for admin.

root$ su - admin  
admin$ touch ~/.sudo_as_admin_successful  
admin$ mkdir ~/.ssh  
admin$ nano ~/.ssh/authorized_keys

# The file will open in the nano text editor.
# Paste your generated ssh key.
# Press Ctrl + X, then Y to save.

# Disable SSH login for root
# 1. In the ssh config file, set PermitRootLogin no.
# 2. Restart the ssh service to refresh its configuration.

root$ nano /etc/ssh/sshd_config  
root$ service ssh restart

# Before exiting this session:
# - in another session, test that root can't ssh login,
# - in another session, test that admin can ssh login.

Install Node.js

The Ghost application runs on Node.js, so I will install that first. NPM, the package manager that practically every node app uses in some form, is distributed along with Node so I don't have to think about it.

# 1. Install curl if not installed already.
# 2. Using curl, download an install script for node.
# 3. Run the install script.

admin$ sudo apt install curl  
admin$ curl -sL -o  
admin$ sudo bash  

Install Ghost

# Installing
admin$ sudo mkdir -p /var/www/  
admin$ cd /var/www/  
admin$ sudo wget  
admin$ sudo tar xfz 0.11.4.tar.gz  
admin$ cd Ghost-0.11.4  
admin$ sudo npm install --production  

That last command, sudo npm install --production, failed for me because its execution required more memory than what my lowly-spec'd droplet could provide. To solve this problem, I added a swap file, sized as large as the total amount of RAM available.

Note: add the commands to create the swap file and make the system use it.

Here, I configure Ghost with my settings, then test if it runs properly.

# Configuring and testing
# 1. Copy the example config file.
# 2. Setup the config file.
#   - Set URL to http://<yourdomain>
#   - Set mail settings
#   - Set server IP to
# 3. Test that Ghost is working.
#   Look for errors in the terminal output.
#   Visit with a browser: http://<yourdomain>:2368

admin$ sudo cp config.example.js config.js  
admin$ sudo nano config.js  
admin$ sudo npm start --production  

Install nginx

Most tutorials I've seen put nginx in front of Ghost, and I wasn't sure why they did that. I think it might have something to do with this. Anyhow, nginx will redirect the http traffic coming through port 80 to Ghost if it targets its set domain.

Note: nginx should be started automatically after installation and on system startup as well.

admin$ sudo apt-get update  
admin$ sudo apt-get install nginx  
admin$ cd /etc/nginx/  
admin$ sudo rm sites-enabled/default  
admin$ sudo touch sites-available/ghost  
admin$ sudo nano sites-available/ghost  
admin$ sudo ln -s /etc/nginx/sites-available/ghost /etc/nginx/sites-enabled/ghost  
admin$ sudo service nginx restart  

Paste the following code block into sites-available/ghost.

server {  
    listen 80;
    server_name <yourdomain>;
    location / {
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   Host      $http_host;

Create user for running Ghost

admin$ sudo adduser --shell /bin/bash --gecos 'Ghost app' ghoster  
admin$ sudo chown -R ghoster:ghoster /var/www/ghost/  
admin$ su - ghoster  
ghoster$ cd /var/www/ghost  
ghoster$ npm start --production  

Test that Ghost works by visiting http://<yourdomain>:2368 in a browser.

Setup forever

Forever is a service built on npm that will automatically restart Ghost if it ever crash on its own.

# 1. Install forever.
# 2. Switch to the ghost user.
# 3. Start an instance of forever that runs Ghost in dev.
# 4. Start an instance of forever that runs Ghost in prod.
# 5. Stop an instace of forever that runs Ghost.

admin$ sudo npm install -g forever  
admin$ su - ghoster  
ghoster$ cd /var/www/ghost/  
ghoster$ forever start index.js  
ghoster$ NODE_ENV=production forever start index.js  
ghoster$ forever stop index.js  

Setup SSL

The SSL protocol is used to encrypt HTTP traffic between web browsers and servers. Nowadays, setting up SSL on a server is almost required, as it addresses security and privary concerns, avoids warnings firing up on browsers, and the cost of using SSL has been drastically reduced with the help of LetsEncrypt.

# 1. Install letsencrypt.
# 2. Edit nginx's ghost config, `/etc/nginx/sites-available/ghost`.
#    Paste the snippet A into the server block we wrote previously.
# 3. Test nginx's ghost config for errors.
# 4. Obtain a single certificate for `domain` and `subdomain`.
#    The cert files will be put in /var/www/ghost/.
# 5. Edit nginx's ghost config to setup SSL using the cert files.

admin$ sudo apt-get install letsencrypt  
admin$ sudo nano /etc/nginx/sites-available/ghost  
admin$ sudo nginx -t  
admin$ letsencrypt certonly --webroot -w /var/www/ghost -d <domain> -d <subdomain>  
admin$ sudo nano /etc/nginx/sites-available/ghost  
admin$ sudo service nginx restart  

Snippet A

location ~ ^/.well-known {  
   root /var/www/ghost;

Snippet B

{{Paste in the config file.}}

Setup automatic SSL renewal

# 1. Test renewal.
# 2. Edit cron's config file to add a job.
#    Paste-in the following Snippet A.

admin$ sudo letsencrypt renew --dry-run --agree-tos  
admin$ sudo crontab -e  

Snippet A

30 2 * * 1 /usr/bin/letsencrypt renew >> /var/log/le-renew.log && /bin/systemctl reload nginx  

Updating Ubuntu

From time to time, a system reboot will be needed to install new system updates. In those cases, don't forget to restart Ghost as well. Nginx should be started automatically on boot, so no need to worry about it.

admin$ sudo apt-get update  
admin$ sudo apt-get upgrade  
admin$ sudo reboot  
admin$ su - ghoster  
admin$ cd /var/www/ghost/  
admin$ NODE_ENV=production forever start index.js  

Updating Ghost

admin$ wget  
admin$ unzip -d ghost-latest/  
# ...
# Copy your theme.
# Copy your database.
# Copy your images.
# Copy your apps.
# Copy your config.js.
# ...

# Setup the new installation.
admin$ cd ghost-latest/  
admin$ npm install --production  
admin$ sudo chown -R ghoster .  
admin$ sudo chmod -R 755 .  
admin$ ls -l 

# Switch the old with the new.
admin$ sudo cp -R ghost-latest /var/www/ghost-latest/  
admin$ cd /var/www/  
admin$ su ghoster  
ghoster$ forever list  
ghoster$ forever stop id  
ghoster$ exit  
admin$ sudo mv ghost ghost.bk  
admin$ sudo mv ghost-latest ghost  
admin$ su ghoster  
ghoster$ cd ghost  
ghoster$ NODE_ENV=production forever start index.js