I recently moved my website from Jekyll + github pages to hosting it myself on ec2 instance. Static page blog with Hugo.

I share following things on my blog

  • Tech information/examples
  • Track - Track and notes
  • Photos - Photos I click
  • Projects

There were various options available to host personal website, My decision to host it on AWS free tier ec2 instance with hugo was based on following factors.

  • Easy to change.
  • Control over content generation and hosting.
  • Use standard/accepted standard to write blogs.
  • Cost - Should cost as less as possible.

This is an attempt to put down my notes from the exercise.

Hugo

Quick start guide on Hugo is very simple : https://gohugo.io/getting-started/quick-start/.
Follow the quick start guide and have a locally running website with hello world content available in < 5 min.
Push the newly created website on github/gitlab.

# Initialize git repository
git init
git add .
git commit -m "<commit message>"
# Add remote : https://help.github.com/en/articles/adding-a-remote and push
git remote add origin <Remote URL>
git push origin master

Buy Domain

Buying a domain name is 3 step process. If you already have a domain name skip this step.

  • Identify if a domain name is available ?
    Goto https://domains.google.com and search for domain you want to buy.
  • Choose Domain provider There are many domain providers - You can use Google, GoDaddy anything that you prefer as per cost.
  • Make payment/Buy Make a payment and have the domain available.

Login to domain provider’s website with your credentials to see domain status.

AWS Account

Now that basic website is ready, Signup for AWS free tier account (If not already signed-up) here - AWS Free Tier.
Once you created the account, Follow the instruction to verify your Email, credit card (This won’t be charged when you use only free tier benefits)
Read about AWS free tier documentation so that you are aware of charges and usage limits.
You will also get an automated call from AWS the next day, which explains about the free tier benefits and limits.

EC2 Instance

Launch Ec2 Instance

Launch Standard instance with launch wizard via Aws console or using cloudformation template.

Instance Type - t2.micro
EBS - 10GB

Elastic IP and update DNS record.

Every AWS account comes with at max 5 elastic IP’s and cost nothing if attached to an instance.
Follow steps in Elastic IP documentation to allocate elastic IP to your ec2 instance.

A Record
DNS records are used to control the location of a resource on the Internet.
As an example, an A Record is used to point a logical domain name, such as “google.com”, to the IP address of Google’s hosting server.

Goto DNS provider’s manage Domain page to add A record for your website.

Add following records

A record
Type - A , Name - @ , Value - Elastic Ip allocated to your ec2 instance

CName
Type - CNAME , Name - www , Value - @

Setup ec2 instance

nginx

We will use nginx as our webserver to host static pages.
Install and configure Amazon Linux nginx

# install
sudo amazon-linux-extras install nginx1.12
# configure to use
cd /etc/nginx/
sudo mkdir -p /etc/nginx/sites-enabled
sudo mkdir -p /etc/nginx/sites-available
# update nginx.conf as per below
sudo vi nginx.conf

nginx.conf : In /etc/nginx

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*.conf;
}

Create a [YourWebsite].conf in /etc/nginx/sites-available/[YourWebsite].conf with following contents.

server {
  listen 80 http default_server;
  listen [::]:80 http default_server;
  root /var/www/[YourWebsite];
  index index.html;
  server_name [YourWebsiteDomainName] www.[YourWebsiteDomainName];
  location / {
    try_files $uri $uri/ =404;
  }
}

Start nginx

# create var/www/[YourWebsite]
sudo mkdir /var/www
sudo chown ec2-user /var/www[YourWebsite]
# link sites available to enabled
sudo ln -s /etc/nginx/sites-available/[YourWebsite].conf \
/etc/nginx/sites-enabled/[YourWebsite].conf
# enable nginx service
sudo systemctl enable nginx
# start it
sudo systemctl start nginx

Deploy website

Now that your ec2 instance is ready to host the static content.
Try visiting the website : http://[YourWebsite]. You will see 404 page served by ngnix.

Remember that in the nginx configuration we mentioned that the static website is hosted under /var/www/[YourWebsite]

You can copy public folder content from hugo to your ec2 instance.

hugo # will generate the static content in /public folder.
cd public
scp -i [ssh-key] -r * ec2-user@[Ec2HostIpAddress]:/var/www/[YourWebsite]

Now if you visit http://[YourWebsite], You will see your website content.
Note : It takes time for DNS record propagation (It might take 15 min or so).

SSL/TLS

SSL/TLS certificates are used to secure network communications and establish the identity of websites over the Internet as well as resources on private networks.

AWS Certificate Manager provides free of cost ACM certificate.
Public hosted zone on the AWS Route 53 is required for generating certificate. Public hosted zone costs $0.50 per hosted zone / month for the first 25 hosted zones.

Another option is to get a free SSL certificate with Lets Encrypt.

Set up

yum install python27-devel git
git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
echo "rsa-key-size = 4096" >> /etc/letsencrypt/config.ini
echo "email = ________@____.com" >> /etc/letsencrypt/config.ini

Certificate generation

Request a certificate the naked domain ([].com) and www subdomain (www.[].com).

# for amazon linux 2 , additional steps to recognize amazon linux.
sudo vim /opt/letsencrypt/certbot-auto

#Find this line in the file (likely near line nr 780): 
#elif [ -f /etc/redhat-release ]; then 
#and replace whole line with this:
elif [ -f /etc/redhat-release ] \ 
|| grep 'cpe:.*:amazon_linux:2' /etc/os-release > /dev/null 2>&1; then

sudo /opt/letsencrypt/certbot-auto certonly --webroot -w \
  /var/www/html -d ________.com -d www.________.com

Certificate files (cert.pem, chain.pem, fullchain.pem, and privkey.pem) are generated in an individual folder for each domain in /etc/letsencrypt/live/ (e.g. /etc/letsencrypt/live/_______.com/ )

  1. cert.pem: server certificate only.
  2. chain.pem: root and intermediate certificates only.
  3. fullchain.pem: combination of server, root and intermediate certificates (replaces cert.pem and chain.pem).
  4. privkey.pem: private key (do not share this with anyone!).

Certificate renewal

Enable auto renewal for certificates with crontab.

sudo nano /etc/crontab

# contents
# Renew SSL Certs
0  13  *  *  *  ec2-user  /opt/letsencrypt/letsencrypt-auto --no-bootstrap renew
# Refresh Server
10 13  *  *  *  root systemctl restart nginx > /dev/null 2>&1

Enable SSL in your nginx conf

Enable http2, SSL, gunzip and rewrite from http to https.

Replace /etc/nginx/sites-available/[YourWebsite].conf with

server {
  listen 443 http2 default_server;
  listen [::]:443 http2 default_server;
  root /var/www/[YourWebsite];
  index index.html;
  server_name [YourWebsite] www.[YourWebsite];
  location / {
    try_files $uri $uri/ =404;
  }
 ssl on;
 ssl_certificate /etc/letsencrypt/live/[YourWebsite]/fullchain.pem;
 ssl_certificate_key /etc/letsencrypt/live/[YourWebsite]/privkey.pem;
 gzip on;
 gzip_types application/javascript image/* text/css;
 gunzip on;
}
server {
       listen 0.0.0.0:80;
       server_name [YourWebsite]
www.[YourWebsite];
       rewrite ^
https://$host$request_uri? permanent;
}

When you visit [YourWebsite], Notice SSL green padlock and redirection to https.

Thanks for reading,

-

Nagesh