📚 Setting Up My Personal Site - Learning Notes

Goal

Host study HTML files on a Hetzner server, accessible over HTTPS on my phone, with a landing page that lists all available topics.

Architecture: Multi-Site with Shared Caddy Proxy

One shared Caddy reverse proxy container handles all domains. Each project is independent.

/root/my-project/
├── caddy-proxy/              # Shared gateway (ports 80/443)
│   ├── docker-compose.yaml
│   └── Caddyfile
├── line-sticker-engine/      # Existing sticker app
│   └── docker-compose.yaml
├── my-personal-site/         # Study materials (static)
│   └── materials/
└── future-project/           # Any future site

Key Concepts Learned

DuckDNS

Virtual Hosting

Caddy

Docker Networking for Multi-Site

docker compose up -d

-d = detached mode. Runs containers in background so you get your terminal back.

Gotcha: Dynamic File Listing Doesn't Work

A browser cannot list files in a directory — JavaScript has no filesystem access. It can only make HTTP requests.

When index.html exists, Caddy serves it instead of a directory listing, even with file_server browse and even with Accept: application/json header.

Solution: Use generate-index.sh to statically build the landing page after adding new files.

Workflow: Adding New Study Material

  1. Drop the .html file into /root/my-project/my-personal-site/materials/
  2. Run ./generate-index.sh
  3. Refresh the page on your phone

Deployment Steps

  1. docker network create caddy-net
  2. Stop old setup: cd /root/my-project/line-sticker-engine && docker compose down
  3. Remove caddy service from sticker app's docker-compose, add caddy-net network
  4. Start sticker app: docker compose up -d
  5. Start shared proxy: cd /root/my-project/caddy-proxy && docker compose up -d
  6. Set DuckDNS subdomain (one-time)
  7. Verify: docker ps and docker logs caddy

Adding a Future Site

  1. Create new project folder with its own docker-compose.yaml
  2. Add caddy-net network to its services
  3. Add a new domain block in caddy-proxy/Caddyfile
  4. For static sites: add a volume mount to caddy instead of a new container
  5. Reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile

Caddyfile Reference

alinesticker.duckdns.org {
    handle_path /api/* {
        reverse_proxy sticker_backend:8000
    }
    handle {
        reverse_proxy sticker_frontend:8501
    }
}

than-is-on.duckdns.org {
    root * /srv/materials
    file_server
}

Firewall Reminder

Ports 80 and 443 must be open in the Hetzner firewall panel.