Server Hosting Notes

Server OS Setup

The server that hosts this website is at Digital Ocean.

To setup access I am using ssh keys, which can be created with the ssh-keygen command, and then copied to the server with ssh-copy-id.

You also want to make sure that the server has been updated recently, which can be done with the apt update && apt upgrade commands on Ubuntu.

Web Server Setup

Most of my career nowadays has been using AWS infrastructure with services like Lambda and ECS/EKS. While this is great for scalability, it is very overkill for a personal website. So for this site I am using a standard web server to serve static files. This was also slightly influenced by reading Aaron Swartz's blog.

Before we set up the web server, we need to setup a firewall. Since we're using Ubuntu, we can use UFW (Uncomplicated Firewall) for this purpose. The following commands will allow SSH and HTTP/HTTPS traffic:

sudo apt install ufw
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status

For the web server, I decided to use Nginx. Luckily, NGINX is super easy to get installed and running. Just run sudo apt-get install nginx. Once it's installed, it automatically starts running. You can check its status with sudo systemctl status nginx. You should be able to visit your server's IP address in a web browser and see the default NGINX welcome page.

So now we have a server. We need to get our website files onto it! The first step is making the directory where the website files will live. By default, NGINX serves files from /var/www/html. Since you always want to assume you're going to run multiple websites on a server, it's a good idea to create a separate directory for each site. So let's create a directory for this site: mkdir /var/www/dougs-site.

And now we have to setup the NGINX configuration for this site. For this site, here is the file:

server {
    listen 80;
    root /var/www/dougs-site;

    server_name dsknr.com www.dsknr.com 157.245.119.227;

    location / {
        try_files $uri $uri/ =404;
    }
}

First we need to put that file in the NGINX sites-available directory: sudo vi /etc/nginx/sites-available/dougs-site. Then we need to create a symbolic link to it in the sites-enabled directory, which NGINX actually uses: sudo ln -s /etc/nginx/sites-available/dougs-site /etc/nginx/sites-enabled/. Finally, we need to test the NGINX configuration for syntax errors and reload NGINX to apply the changes: sudo nginx -t && sudo systemctl reload nginx.

Now we need to get the actual files there! For now we're going to use rsync:

rsync -avz --delete ./* tinysite:/var/www/dougs-site/

And this let's you go to the IP address and see the files!

Automated Deployment with GitHub Actions

While manual deployment with rsync works fine, it's much more convenient to have deployments happen automatically when code is pushed to the repository. GitHub Actions can handle this automatically.

The workflow is configured in .github/workflows/deploy.yml and runs on every push to the master branch. Here's how it works:

name: Deploy to Server

on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy_key
          chmod 600 ~/.ssh/deploy_key
          ssh-keyscan -H ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts

      - name: Deploy with rsync
        run: |
          rsync -avz --delete \
            -e "ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no" \
            --exclude '.git' \
            --exclude '.github' \
            --exclude 'README.md' \
            ./ ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }}:${{ secrets.SERVER_PATH }}

      - name: Cleanup
        if: always()
        run: rm -f ~/.ssh/deploy_key

The workflow uses GitHub Secrets to securely store sensitive information. You need to configure these secrets in your repository settings under Settings → Secrets and variables → Actions:

The workflow uses the same rsync command as manual deployment but with the --delete flag to remove files on the server that don't exist in the repository. It excludes Git-related files and the README to keep the deployment clean.

Now, we need to get our domain name set up. I have my domain hosted with Namecheap, so I will log into my Namecheap account and update the DNS settings to point to my server's IP address.

Finally, in this day and age, every website should be behind HTTPS with a valid SSL certificate. You can use Let's Encrypt to obtain a free SSL certificate and configure NGINX to use it. To install Certbot run:

sudo snap install --classic certbot

And now we need to make the command executable: sudo ln -s /snap/bin/certbot /usr/local/bin/certbot

And then finally run certbot: sudo certbot --nginx