Article illustration 1

For developers weary of opaque cloud pricing and abstraction layers, a return to self-managed infrastructure offers refreshing control—and significant cost savings. One engineer's journey from AWS to a Hetzner VPS culminated in an elegantly simple deployment pipeline for Hugo static sites, leveraging Caddy's automation prowess and GitHub Actions for frictionless updates. Here’s how this stack delivers enterprise-grade performance without enterprise-grade bills.

Why Hetzner + Caddy + Hugo?

Hugo generates lightweight static sites, eliminating backend complexity. Pairing it with Hetzner’s affordable virtual private servers (often 80% cheaper than comparable AWS EC2 instances) reduces overhead. Caddy, the modern web server, seals the deal with automatic TLS certificate management via Let’s Encrypt—no manual openssl commands required.

Core Implementation

1. Server Setup & Caddy Installation
Begin by provisioning a Hetzner VPS (Ubuntu/Debian). Caddy installs via its official repository:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | \
  sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | \
  sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

Enable Caddy as a persistent service:

sudo systemctl enable --now caddy

2. Directory Structure & Permissions
Create the site directory and assign ownership to Caddy:

sudo mkdir -p /var/www/blog
sudo chown -R caddy:caddy /var/www/blog
sudo chmod -R 755 /var/www/blog

3. Caddyfile Configuration
Edit /etc/caddy/Caddyfile to define the site. This configuration enables Gzip compression, file serving, and access logs:

your-domain.com {
    root * /var/www/blog
    encode gzip
    file_server

    log {
        output file /var/log/caddy/blog.log
    }

    header ?Cache-Control "max-age=3600"
}

Reload Caddy: sudo systemctl reload caddy. DNS propagation later, Caddy auto-provisions HTTPS certificates.

Automating Deployments with GitHub Actions

Manual rsync works but lacks scalability. This GitHub Actions workflow builds Hugo and deploys via SSH:

name: Build and Deploy

on:
  push:
    branches: [master]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Hugo
        run: |
          wget https://github.com/gohugoio/hugo/releases/download/v${{ env.HUGO_VERSION }}/hugo_extended_${{ env.HUGO_VERSION }}_Linux-amd64.tar.gz
          tar -xzf hugo*.tar.gz hugo
          sudo mv hugo /usr/local/bin/
        env:
          HUGO_VERSION: 0.125.3

      - name: Build Site
        run: hugo --environment production

      - name: Deploy via rsync
        uses: burnett01/[email protected]
        with:
          switches: -avz --delete
          path: public/
          remote_path: /var/www/blog/
          remote_host: ${{ secrets.SSH_HOST }}
          remote_user: ${{ secrets.SSH_USER }}
          remote_key: ${{ secrets.SSH_PRIVATE_KEY }}

Secrets Configuration:
- SSH_PRIVATE_KEY: Private key for server access
- SSH_HOST: Server IP/hostname
- SSH_USER: Server username

Generate a deployment key with ssh-keygen, then add the public key to ~/.ssh/authorized_keys on your Hetzner VPS.

Why This Stack Wins

  • Cost Efficiency: Hetzner VPS costs ~€5/month vs. AWS’s $15+ for similar specs
  • Zero-Touch HTTPS: Caddy automates certificate renewals
  • Atomic Deployments: rsync --delete ensures server/file parity
  • Infrastructure Control: Direct server access enables custom firewalls, monitoring, or Docker integrations
  • CI/CD Simplicity: GitHub Actions triggers builds on git push

Troubleshooting Essentials

  • Caddy Failing? Check logs: journalctl -u caddy -f
  • Permission Denied? Verify ownership: ls -la /var/www/blog
  • HTTPS Not Working? Confirm DNS points to your VPS IP and port 443 is open

This setup proves that for static sites, complexity doesn’t equal capability. By combining Hugo’s speed, Hetzner’s affordability, Caddy’s automation, and GitHub’s CI/CD, developers regain both control and cash—without sacrificing reliability.

Source: Pliutau.com