Skip to content
Portfolio

Hetzner VPS & Docker Foundation

The foundation of this project is a Hetzner Virtual Private Server (VPS). All services are containerized with Docker and managed via Docker Compose for reproducible deployments.

  • Provider: Hetzner Cloud (EU-based, GDPR-applicable infrastructure)
  • OS: Ubuntu 24.04 LTS
  • Location: Nuremberg, Germany — low latency for users in the DACH region

Before deploying containers, the host requires basic security hardening.

Password authentication is disabled in favor of cryptographic keys. SSH is not reachable on the server’s public IP; it is available only via the Tailscale overlay (100.x.x.x). The Hetzner firewall rules and Tailscale setup are detailed in chapters 3 and 5.

Terminal window
# Connecting via the Tailscale interface (username comes from GitHub Secrets in CI)
ssh $USER@100.x.x.x
apt update && apt upgrade -y

Terminal window
docker --version
docker compose version

The infrastructure uses modular docker-compose.yml files per service. Containers that receive traffic from Nginx Proxy Manager join an external Docker network named proxy-network.

Static builds for the Astro portfolio and Starlight documentation are served by lightweight Alpine Nginx containers instead of Node.js runtime servers:

# Snippet: Static Site Containers
services:
web-portfolio:
image: nginx:alpine
container_name: astro-site
restart: unless-stopped
networks:
- proxy-network
volumes:
- ./public_html:/usr/share/nginx/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
web-docs:
image: nginx:alpine
container_name: starlight-docs
restart: unless-stopped
networks:
- proxy-network
volumes:
- ./docs:/usr/share/nginx/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro

  • docker info and docker compose version run without permission errors.
  • docker ps shows astro-site, starlight-docs, and nginx-proxy-manager in a running state.
  • SSH over the public IP times out; SSH over Tailscale (100.x.x.x) succeeds only from an authenticated device.

Proceed to Cloudflare DNS & Edge Routing to see how public traffic is directed to this server.