Skip to content

vreshch/infrastructure

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Infrastructure as Code - Multi-Domain Docker Swarm on Hetzner Cloud

Terraform Docker Swarm Hetzner Cloud License

Production-ready single-server platform for hosting multiple projects. Provisions a Hetzner Cloud server with Docker Swarm, Traefik reverse proxy, and DNS records for N domains — all via a single hcloud provider. Each project deploys itself via its own CI/CD.

Architecture

Single hcloud provider (v1.56+)
|-- hcloud_server           -> cx43 (16 GB, 8 vCPU)
|-- hcloud_firewall         -> ports 22, 80, 443 + Docker Swarm
|-- hcloud_ssh_key          -> SSH access
|-- hcloud_volume           -> optional extra disk (/mnt/data)
|-- hcloud_zone_rrset (A)   -> vreshch.com, diffractwd.com, mcpxhub.io, crystallography.io
|-- hcloud_zone_rrset (A)   -> dev.mcpxhub.io (subdomain)
|-- hcloud_zone_rrset (CNAME) -> www.vreshch.com, www.diffractwd.com
\-- hcloud_zone_rrset (CNAME) -> traefik.X, swarmpit.X, logs.X (admin subdomains)

Deploy Flow

Infrastructure repo:  terraform apply -> server + DNS + Traefik + Swarmpit + Dozzle
Each project repo:    GitHub Actions -> build image -> SSH -> docker stack deploy

This repo only provisions the platform. Each project (vreshch.com, mcpxhub.io, etc.) has its own docker-compose.yml and CI/CD pipeline that deploys to this server.

Features

  • Single Providerhcloud handles both compute and DNS (no third-party DNS provider)
  • Multi-Domain DNS — A records for N domains, all pointing to the same server
  • Optional Volume — Extra disk for large datasets (e.g., crystallography.io COD data)
  • Secure by Default — Firewall (SSH + HTTP/S only), automatic SSL via Let's Encrypt, bcrypt auth
  • Built-in Monitoring — Traefik dashboard, Swarmpit UI, Dozzle logs
  • No App Stacks — Each project deploys itself; this repo is infrastructure only

Quick Start

Prerequisites

1. Clone and configure

git clone https://github.com/YOUR_USERNAME/infrastructure.git
cd infrastructure

# Interactive setup — collects all config, generates password hash
./scripts/setup-fill-tfvars.sh prod

2. Deploy

cd terraform
terraform init
terraform plan -var-file="terraform.prod.tfvars"
terraform apply -var-file="terraform.prod.tfvars"

3. Access admin tools (after 5-10 min for DNS + SSL)

  • Traefik Dashboard: https://traefik.yourdomain.com
  • Swarmpit UI: https://swarmpit.yourdomain.com
  • Dozzle Logs: https://logs.yourdomain.com

Configuration

Required Variables

# Single Hetzner Cloud token (compute + DNS)
hetzner_token = "your-cloud-api-token"

# Domains (zones must already exist in Hetzner DNS)
domains = [
  { name = "vreshch.com",        enable_www = true },
  { name = "diffractwd.com",     enable_www = true },
  { name = "mcpxhub.io",         enable_www = false, subdomains = ["dev"] },
  { name = "crystallography.io", enable_www = false },
]

# Admin services — subdomains on any domain you own
admin_domain       = "vreshch.com"
traefik_host       = "traefik.vreshch.com"
swarmpit_host      = "swarmpit.vreshch.com"
dozzle_host        = "logs.vreshch.com"
traefik_acme_email = "admin@vreshch.com"

# Server
server_name = "prod-server"
server_type = "cx43"
location    = "nbg1"

# SSH keys
ssh_public_key  = "ssh-ed25519 AAAAC3..."
ssh_private_key = "-----BEGIN OPENSSH PRIVATE KEY-----\n..."

# Auth (base64-encoded htpasswd)
admin_password_hash = "YWRtaW46JDJ5JDA1JC4uLg=="

# Optional: extra disk for large datasets
enable_volume     = true
volume_size       = 100
volume_mount_path = "/mnt/data"

Domain Variable

Each entry in domains creates:

  • An A record for @ pointing to the server IP
  • A www CNAME (if enable_www = true)
  • A records for each subdomain in subdomains list

Admin subdomains (traefik_host, swarmpit_host, dozzle_host) are created as CNAMEs on the admin_domain.

Supported Server Types

Type vCPU RAM Storage Recommended For
cx23 2 4 GB 40 GB Development
cx33 4 8 GB 80 GB Staging
cx43 8 16 GB 160 GB Production
cx53 16 32 GB 320 GB High Traffic

Repository Structure

.
|-- terraform/                  # Terraform infrastructure code
|   |-- main.tf                 # Compute module + DNS resources (hcloud_zone_rrset)
|   |-- variables.tf            # Variable definitions (domains, volume, etc.)
|   |-- outputs.tf              # Output definitions
|   |-- versions.tf             # Provider configuration (hcloud ~> 1.56)
|   \-- modules/
|       \-- compute/            # Server, firewall, SSH key, volume
|           |-- main.tf
|           |-- variables.tf
|           |-- outputs.tf
|           \-- scripts/        # Server initialization scripts
|               |-- init-docker.sh
|               |-- init-docker-swarm.sh
|               \-- deploy-services.sh
|-- configs/                    # Environment configuration examples
|   |-- dev.example.tfvars
|   \-- prod.example.tfvars
|-- scripts/                    # Automation scripts
|   |-- setup-env.sh            # Interactive environment setup
|   |-- setup-fill-tfvars.sh    # One-step config + password generation
|   |-- deploy-env.sh           # Multi-environment deployment
|   \-- utils/
|       |-- generate-ssh-keys.sh
|       |-- generate-password.sh
|       \-- validate-config.sh
|-- docs/                       # Documentation
\-- README.md

How Apps Deploy Themselves

This infrastructure repo does not manage application stacks. Each project has its own deployment:

# Example: project's GitHub Actions workflow
- name: Deploy to server
  run: |
    ssh root@${{ secrets.SERVER_IP }} << 'EOF'
      docker stack deploy -c docker-compose.yml myapp
    EOF

The only shared resource is the traefik-public overlay network, which Traefik uses to discover and route to services. Each project's docker-compose.yml should attach its public-facing service to this network and add Traefik labels.

Scripts

Script Description
setup-fill-tfvars.sh <env> Interactive config + password hash generation
setup-env.sh <env> Interactive or template-based env setup
deploy-env.sh <env> <action> Deploy/plan/destroy with backend selection
utils/generate-ssh-keys.sh Generate SSH key pairs
utils/generate-password.sh Generate bcrypt password hashes
utils/validate-config.sh Validate tfvars before deployment

Security

  • Firewall: Only ports 22, 80, 443 exposed (Swarm ports restricted to private networks)
  • SSL/TLS: Automatic certificates via Let's Encrypt
  • Authentication: Admin tools protected with bcrypt password hashing
  • SSH: Key-based authentication only
  • Secrets: Never committed to Git (.gitignore configured)

Troubleshooting

Terraform init fails

cd terraform/
rm -rf .terraform/ .terraform.lock.hcl
terraform init

DNS not resolving

# Check DNS records
dig +short yourdomain.com

# Verify zone exists in Hetzner DNS console

Services not accessible

ssh root@YOUR_SERVER_IP
docker service ls
docker service logs traefik

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Infrastructure as Code - Docker Swarm on Hetzner Cloud

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors