After creating a new instance on a VPS and establishing ssh access to this server environment, the next step would be launching a server to host website. The two most popular server protocols are Apache and Nginx. Apache used to be the dominant choice but in recent years Nginx has caught up quickly and is now more popular than Apache among the biggest websites in the world. We'll go with Nginx.

1) Associate domain name to server IP

First we need to tell hosting platform to point the domain name (for example site-name) to the public IP of the virtual machine 123.456.789.000 (let's call it athena). I'm on Linux distro Ubuntu 20.04.

In the DNS Zone file (managed by domain registrar such as Godaddy) for the domain name site-name, create the following entries:

Type Hostname Value TTL (seconds)
A @ 123.456.789.000 60
A www 123.456.789.000 60

Though TTL (Time To Live) is set at the lowest possible value of 60 seconds, the actual propagation of the new A record through out layers of the Internet may take longer than that. The next step is optional but would be helpful to speed up the process.

In athena, open the hosts configuration file

sudo vim /etc/hosts

Add this entry and save the file

123.456.789.000 site-name

Back to the local Mac (I'm on macOS Big Sur 11.2.0), flush the local DNS cache, so that local Mac would not continue to point the IP address to its old domain name.

sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder

2) Set up Nginx Server

Login to athea via a user with root access and install Nginx.

sudo apt-get install nginx

To start Nginx

sudo systemctl start nginx

To stop Nginx

sudo systemctl stop nginx

To restart Nginx

sudo systemctl restart nginx

To reload Nginx without restarting

sudo systemctl reload nginx

Check if Nginx server is now running

service nginx status


systemctl is-active nginx

3) Set up firewall

Firewall is managed through UFW (or Uncomplicated Firewall) utility.  It should be installed by default as part of the standard packages from Ubuntu. If it's uninstalled for some reason, install it with

sudo apt-get install ufw

Enable firewall

sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup

When firewall is enabled, other connections will be lost. We need to tell UFW to allow access for Nginx and ssh.

sudo ufw allow 'Nginx Full'
sudo ufw allow ssh

Reload UFW to turn on the changes

sudo ufw reload

Check UFW status

sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
80,443/tcp (Nginx Full)    ALLOW IN    Anywhere
22/tcp                     ALLOW IN    Anywhere
80,443/tcp (Nginx Full (v6)) ALLOW IN    Anywhere (v6)
22/tcp (v6)                ALLOW IN    Anywhere (v6)

To stop the firewall

sudo ufw disable

UFW's configuration files can be accessed at /etc/ufw on Ubuntu 20.04.

4) Set up directory structure for the website

Usually the files that host websites are put under /var/www in a typical Ubuntu instance.

mkdir -p /var/www
mkdir -p /var/www/site-name
mkdir -p /var/www/site-name/public_html
mkdir -p /var/www/site-name/log
mkdir -p /var/www/site-name/backups

Make sure Nginx can access www with the right permission

chmod 0755 ~/var/www

5) Configure Nginx for the new website

cd /etc/nginx

Before making any changes, make a backup copy of the important configuration file nginx.conf

sudo cp nginx.conf nginx.conf.bak.YYYYMMDD

Take a look at the currently activated websites

cd /etc/nginx/sites-enabled/
ls -la
drwxr-xr-x 2 root root 4096 Jun 13 04:48 .
drwxr-xr-x 8 root root 4096 Jun 13 05:04 ..
lrwxrwxrwx 1 root root   34 Jun 13 04:48 default -> /etc/nginx/sites-available/default

No website is activated besides the default one. Let's enable a new website for site-name.

Enter into sites-available directory

cd /etc/nginx/sites-available

There is already a default configuration file in this directory that can be used as a guide/reference as it explains many frequently used parameters.

Create a new configuration file for the website site-name

sudo vim site-name

Copy and paste the following into configuration file site-name with appropriate changes. /var/www/site-name/public_html is the path that contain the files for the public website where index.html will be served to usher viewers into the landing page of the website. server-name should be the exact domain name that you control, such as sitename.

server {
        listen 80;
        server_name site-name;
        root /var/www/site-name/public_html;
        index index.html;

Create a symbolic link to enable the website site-name

sudo ln -s /etc/nginx/sites-available/site-name /etc/nginx/sites-enabled/

When making future changes in the configuration file for site-name, changes should be made in the site-name configuration file in directory sites-available. The link in sites-enabled points to the configuration file in sites-available directory.

6) Create a dummy landing page

Let's create a bare-bone website to test things out

sudo vim /var/www/site-name/public_html/index.html

Type something simple into index.html

Hello World! Welcome to Site-Name!

Open this website https://site-name in a browser to check if the landing page can be loaded properly.

Though not showing anything meaningful yet at this point, the website is now up and running. All the changes to index.html will be reflected when https://site-name is visited or refreshed.

7) Turn off logs

Logging can take up substantial disk storage and system bandwidth. To turn it off

sudo vim /etc/nginx/nginx.conf

Find the logging settings and add # to comment them out

  # Logging Settings

  #access_log /var/log/nginx/access.log;
  #error_log /var/log/nginx/error.log;

8) Set up HTTPS

It's critically important to enable the secure HTTP protocol on a newly launched website, otherwise data will be transmitted in plain text. Every URL should now start with https rather than good old http. Increasingly the most popular browsers will deem an http address as a security risk and even prevent it from being visited. In HTTPS, the communication protocol is encrypted using SSL (Secure Sockets Layer). In the past, enabling SSL could be quite a manual process as a certificate needs to be generated to authenticate the website. With the wonderful (and free!) Let's Encrypt tool, this pain is largely alleviated.

sudo apt-get install software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install python3-certbot-nginx

Run certbot

sudo certbot --nginx

Which brings up the below dialog. Certbot will automatically look for enabled sites in /etc/nginx directory. Just follow the on-screen instructions to complete the setup.

Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: site-name
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel):

If all goes well, you will see a congratulations message when the certificate is generated successfully for the chosen sites. Restart the Nginx server to make the new changes effective

sudo systemctl restart nginx

If need to renew the certificate

sudo certbot renew

When you install the certificates using certbot it automatically creates cron job to renew certificates. For Ubuntu this cron job can be found at /etc/cron.d/certbot

cat /etc/cron.d/certbot
# /etc/cron.d/certbot: crontab entries for the certbot package
# Upstream recommends attempting renewal twice a day
# Eventually, this will be an opportunity to validate certificates
# haven't been revoked, etc.  Renewal will only occur if expiration
# is within 30 days.
# Important Note!  This cronjob will NOT be executed if you are
# running systemd as your init system.  If you are running systemd,
# the cronjob.timer function takes precedence over this cronjob.  For
# more details, see the systemd.timer manpage, or use systemctl show
# certbot.timer.

0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew

This cron job gets triggered twice every day to renew certificates. certbot -q renew will check if certificate is expiring in the next 30 days. If it is, it will auto renew it quietly without generating output. If it is not expiring, then it will not perform any action. The same information such as email, domain name, web server root will be provided when renewing the certificate.

9) Enable HTTP/2

It's a good practice to enable HTTP/2 for stronger security

sudo vim /etc/nginx/sites-available/site-name

Find the listen line for certbot

listen 443 ssl;

Change that to

listen 443 ssl http2;

Restart the Nginx server

sudo systemctl restart nginx

Voila! Now the website at https:/// is up and running and presented with view from index.html file with other contents (such as images) served from /var/www/site-name/public_html/ directory in a virtual instance hosted on server managed with public IP 123.456.789.000. Next step would be turning index.html into something for real and set up a continuous integration process for code deployment, but that would be another topic for another day.

1. Thanks for reading! newsletter shares the learning and discovery for aspiring polymath, on crypto, blockchain, investment, startups, productivity hacks and technology in general. If you're forwarded this email, you can sign up for the free newsletter here or just press the "Subscribe" button to receive the latest articles directly in your inbox. The free tier would be just fine to receive all available contents.
2. You're welcome to share your feedback and comments in the comment section. No email registration is needed. You can even do so anonymously.
3. If you find this article helpful, please share it with like-minded friends. Thank you.