Linode, HTTP/HTTPS & Deployment | /var/log/share

Linode, HTTP/HTTPS & Deployment

It’s been long since I have posted something and it feels good to start writing again. I thought it made sense to not host this on GitHub anymore. If maintaining this cost me money, I thought it might make me more serious about maintaining it. Most likely I didn’t need to but this post is all about the migration and my thought process behind it.

Why Linode

There are multiple hosting providers out there and I choose Linode mainly because of the cost and the hardware resources it provided me along with it. Currently, t2 micro instance on AWS would set me around $9.50 with 1GiB of RAM. With a 10$ fixed rate per month, I can get 2GB of RAM and 50GB in storage for Linode. I could go with a nano instance on AWS and save quite a bit, but I occasionally like to code with my iPad. So, having a decently sized box would be beneficial.

Linode has pretty decent documentation for hosting your website. They do have issues with some steps along the way, So this guide will probably help you avoid the pitfalls. Once you have registered on Linode, spin up your instance (preferably close to where you live). Pro Tip: setup ssh keys so you can easily jump into your box without needing to input your password.

ssh-keygen -t rsa

This command should generate an ssh key (use a passphrase for extra security) which you can add in Linode manager when you spin up your instance.

Purchasing your domain & Setting up DNS A records

You now have your instance spin up, but before moving ahead - you need to purchase your domain. Once, you have purchased this - we need to let your domain provider know which nameservers to use. This is used later for DNS resolution. Follow this guide here to get the list of nameservers you need to use when using Linode.

You are not done yet since there is more to DNS than meets the eye. Right now, your browser knows which nameservers to connect to when someone goes to <your_domain>.com but you have to setup an A record to let the nameservers know which machines your files are on. This is where your public IP for the instance you spun up is useful. This guide explains how to do this on Linode. Pro Tip: Leave hostname as blank for one record if you wish to proxy <your_domain>.com correctly without www. The guide has an example for www. If you’re lazy about it - just put * along with the blank option - to route all subdomains and <your_domain>.com.

This step is the most time-consuming. It takes about 24 hours for the DNS resolution to take place for your nameservers or worse case - 48 hours. On the other hand, A record resolution takes around 2 hours. Once you’re done with this - you can head to the next section to bootstrap your server and serve your website.

Apache server and SSL

While you’re waiting on your DNS resolution to complete, you can go ahead and bootstrap other bits on your Linode box. I choose Apache server here but you can also use the popular option - Nginx. Frankly, I would prefer Nginx here since it should be more light-weight for static websites. Here’s the guide for doing this on an Ubuntu box. I skipped the part about setting up databases since I don’t require it. There’s also a guide for Nginx there, so you can follow that if you choose that.

Setup a sample index.html page containing Hello World at /var/www/html/example.com/public_html, so you can test out apache settings by doing curl -X GET localhost:80. The next bits of steps involve waiting for the DNS resolution to take place. You can use dns-checker and verify for both A record check and nameservers check. Once nameservers resolve, A record should also resolve pretty soon.

Once you have all the bits above working - you can use lets-encrypt to get yourself ssl certificate. Follow this guide here to get yourself an ssl certificate. Pro Tip: stop apach2 service because lets-encrypt cannot have anything bouund to port 80 while it’s issuing the certificate. The command should spit out both the certificate and the private key you need to setup rest of apache configs.

You will likely run into a problem before all of this. Once, DNS resolution finished it should be possible for you to check your website using your browser. If you followed this guide - you should have the hello world page display up. You will need to use http for now since we haven’t added the settings in apache to enable ssl yet.

If not, you need to setup your ip tables to accept traffic on two ports 80 and 443. 80 will be used for HTTP traffic and 443 for HTTPS.

$ sudo iptables -A INPUT -p tcp -m multiport --dports 80,443 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

$ sudo iptables -A OUTPUT -p tcp -m multiport --dports 80,443 -m conntrack --ctstate ESTABLISHED -j ACCEPT

The commands above should open up the connections to these ports. So far, what we have is:

The final bits you need to do is follow this guide to enable HTTPS connection on your apache server. If you followed this guide until now - you should have your website setup on the Linode machine you spun up. The output command from lets-encrypt will have the private key and certificate information you need to use for SSLCertificateFile and SSLCertificateKeyFile in the config.

Building your static website

Let’s now look at the final piece of the puzzle. Static website generator are available on the web and the easiest IMO to use is what Github uses for static websites. Jekyll is pretty easy to setup and some templates use markdown to create blog posts. This website also uses the same methodology. Here is a good example to follow. bundle exec jekyll build generates all the necessary resources in _site folder within your working directory. This directory contains all the files you need to put in your apache webserver to host your website.

So, the next part of the blog talks about how I ended up automating this workflow for now. I haven’t used the actions flow yet in Github but have a fairly vague idea of how I would go about doing this. For now, this is done through a script and I will give this recipe out before using Github actions in another post. I love Makefiles and for I am going to use that here.

The most common actions I end up doing is build, clean and serve so I set this up as Makefile recipes

.PHONY: build
build:
	bundle exec jekyll build

.PHONY: clean
clean:
	bundle exec jekyll clean
	rm -rf _site/

.PHONY: deploy
deploy: build _site/
	@. deploy.sh && deploy

.PHONY: local-build
local-build: build
	bundle exec jekyll serve

The important recipe here is deploy. Here, I’m saying in order to execute this recipe - we need to execute build first and depend on the folder _site to exist. The rest of the workload is now managed by a shell script. The script looks something like this:

#!/bin/bash
SERVER="XX.XX.XX.XX"
USER="root"
HOME="/home/XXXX" # Ideally this should not be root but a user who should have access to apache directory space.

deploy() {
    echo "Starting deployment to ${SERVER}...\n"
    echo "About to tar website folder.."
    tar czf site.tar.gz _site/

    # First clear any deployment material already on the node
    # We use the home folder as the staging directory.
    ssh -A $USER@$SERVER "
        rm -rf ${HOME}/site.tar.gz;
        rm -rf ${HOME}/site
        rm ${HOME}/untar.log
    "
    scp site.tar.gz $USER@$SERVER:$HOME/

    # Clear all the contents inside public_html dir and copy over the files you just untarred.
    ssh -A $USER@$SERVER "
        echo \"About to untar deployment dir\"
        cd ${HOME}
        tar xvf ${HOME}/site.tar.gz > untar.log && echo done
        rm -r /var/www/html/<your_domain>/public_html/*
        cp -r ${HOME}/_site/* /var/www/html/<your_domain>/public_html/
    "
}

Comments in the code should help you tell what I am doing. This is a fairly simple script that does some barebones folder manipulation to get the job done. This is all I have for now, I might come up with a new blog post to see how Github actions fare and if I can manage to do this entirely in Github itself. The main challenge here is to ensure I have a less privileged user when ssh(ing) from Github workflow. From past experiences, I have found it easier to build workflows in makefile and move this to automated workflow like jenkins or any other build system like travis, circleci.