Production-ready Docker Swarm infrastructure on Hetzner Cloud with automated deployment. Supports both local Terraform and CI/CD workflows.
- 🚀 Automated Deployment - Deploy via scripts or integrate with GitHub Actions
- 🌍 Multi-Environment - Dev, staging, and production support
- 🔒 Secure by Default - SSL certificates, firewall rules, and password protection
- 📊 Built-in Monitoring - Traefik dashboard, Swarmpit UI, and Dozzle logs
- 💰 Cost-Effective - Start from €8/month, scale as needed
- 🔧 Interactive Setup - Guided configuration with validation
- 🛠️ Utility Scripts - SSH key generation, password hashing, config validation
Local/CI → Terraform → Hetzner Cloud
↓
┌───────────┼───────────────┐
│ │ │
Development Staging Production
(cx23 4GB) (cx33 8GB) (cx43 16GB)
€2.99/mo €4.99/mo €8.99/mo
Included Services:
- Traefik - Automatic SSL/TLS and reverse proxy
- Swarmpit - Docker Swarm management UI
- Dozzle - Real-time container log viewer
- Automatic DNS - Managed via Hetzner DNS API
- Hetzner Cloud account with API token
- Hetzner DNS account with API token
- Domain registered and configured in Hetzner DNS
- Terraform >= 1.12 (for local deployment)
-
Clone this repository
git clone https://github.com/YOUR_USERNAME/infrastructure.git cd infrastructure -
Run the interactive setup script
# Single command that collects all configuration ./scripts/setup-fill-tfvars.sh prodThe script will prompt for:
- Domain name and subdomains
- Hetzner Cloud & DNS API tokens (Get tokens)
- DNS Zone ID (Find in DNS console)
- Server configuration (name, type, location)
- SSH key paths
- Admin password (or auto-generate)
It automatically:
- Validates all inputs
- Generates bcrypt password hash
- Base64-encodes credentials for Terraform
- Creates
terraform/terraform.prod.tfvarswith secure permissions
-
Deploy infrastructure
cd terraform terraform init terraform plan -var-file="terraform.prod.tfvars" terraform apply -var-file="terraform.prod.tfvars"
-
Access your services (after 5-10 minutes for DNS + SSL) for server access ./scripts/utils/generate-ssh-keys.sh deploy ed25519
./scripts/utils/generate-password.sh admin
-
Setup environment configuration (Interactive)
# Interactive mode - prompts for all values ./scripts/setup-env.sh dev # Or use template mode ./scripts/setup-env.sh dev --from-template # Then edit: nano terraform/terraform.dev.tfvars
-
Validate configuration
./scripts/utils/validate-config.sh terraform/terraform.dev.tfvars
-
Deploy infrastructure
# With local backend ./scripts/deploy-env.sh dev apply --local # Or with Terraform Cloud (configure versions.tf first) ./scripts/deploy-env.sh dev apply
-
Access your services
After deployment (5-10 minutes), access your management tools:
- Traefik Dashboard:
https://admin.yourdomain.com - Swarmpit UI:
https://swarmpit.yourdomain.com - Dozzle Logs:
https://logs.yourdomain.com
- Traefik Dashboard:
-
Access your services (after 5-10 minutes for DNS + SSL)
- Traefik Dashboard:
https://admin.yourdomain.com - Swarmpit UI:
https://swarmpit.yourdomain.com - Dozzle Logs:
https://logs.yourdomain.com
Login credentials:
admin/<your-generated-password>Note: SSL certificates are issued automatically but require DNS to propagate first (5-10 min).
- Traefik Dashboard:
Click to expand manual setup steps
# 1. Generate SSH keys
./scripts/utils/generate-ssh-keys.sh deploy ed25519
# 2. Setup configuration interactively
./scripts/setup-env.sh prod
# 3. Validate
./scripts/utils/validate-config.sh terraform/terraform.prod.tfvars
# 4. Deploy
./scripts/deploy-env.sh prod apply --local.
├── terraform/ # Terraform infrastructure code
│ ├── main.tf # Main infrastructure definition
│ ├── variables.tf # Variable definitions with validation
│ ├── outputs.tf # Output definitions
│ ├── versions.tf # Provider and backend configuration
│ └── modules/ # Reusable modules
│ ├── compute/ # Server provisioning module
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ └── scripts/ # Server initialization scripts
│ │ ├── init-docker.sh
│ │ ├── init-docker-swarm.sh
│ │ └── deploy-services.sh
│ └── dns/ # DNS management module
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── configs/ # Environment configurations
│ ├── template.tfvars # Master configuration template
│ ├── dev.example.tfvars # Development environment template
│ ├── prod.example.tfvars # Production environment template
│ └── terraform.example.tfvars # Legacy template
├── scripts/ # Automation scripts
│ ├── setup-env.sh # Interactive environment setup
│ ├── deploy-env.sh # Multi-environment deployment
│ └── utils/ # Utility scripts
│ ├── generate-ssh-keys.sh # SSH key generation
│ ├── generate-password.sh # Password hash generation
│ └── validate-config.sh # Configuration validation
├── docs/ # Documentation
│ ├── quickstart.md # 5-minute setup guide
│ ├── configuration.md # Configuration reference
│ ├── deployment.md # Deployment guide
│ └── troubleshooting.md # Common issues and solutions
└── README.md # This file
# Create configuration interactively
./scripts/setup-env.sh staging
# Or copy and edit template
cp configs/prod.example.tfvars terraform/terraform.prod.tfvars
nano terraform/terraform.prod.tfvars
# Validate configuration
./scripts/utils/validate-config.sh terraform/terraform.prod.tfvars
# Deploy
./scripts/deploy-env.sh prod apply --local# Modify configuration
nano terraform/terraform.prod.tfvars
# Preview changes
./scripts/deploy-env.sh prod plan --local
# Apply changes
./scripts/deploy-env.sh prod apply --local# Destroy infrastructure (requires confirmation)
./scripts/deploy-env.sh dev destroy --local# View deployment information
./scripts/deploy-env.sh dev outputEach environment requires a configuration file with these values:
# Hetzner Credentials
hetzner_token = "your-cloud-api-token"
hetzner_dns_token = "your-dns-api-token"
hetzner_dns_zone_id = "your-zone-id"
# Domain Configuration
domain_name = "yourdomain.com"
traefik_host = "admin.yourdomain.com"
swarmpit_host = "swarmpit.yourdomain.com"
dozzle_host = "logs.yourdomain.com"
traefik_acme_email = "admin@yourdomain.com"
# Server Configuration
server_name = "my-server"
server_type = "cx22"
location = "nbg1"
environment = "development"
# SSH Keys
ssh_public_key = "ssh-ed25519 AAAAC3..."
ssh_private_key = "-----BEGIN OPENSSH PRIVATE KEY-----\n..."
# Authentication
admin_password_hash = "admin:$2y$05$..."See docs/configuration.md for detailed explanations of all variables.
| Type | vCPU | RAM | Storage | Price/month (approx.) | Recommended For |
|---|---|---|---|---|---|
| cx23 | 2 | 4 GB | 40 GB | €2.99 | Development |
| cx33 | 4 | 8 GB | 80 GB | €4.99 | Staging |
| cx43 | 8 | 16 GB | 160 GB | €8.99 | Production |
| cx53 | 16 | 32 GB | 320 GB | €16.99 | High Traffic |
Additional costs:
- DNS Zones: Free
- Snapshots: €0.01/GB per month (optional)
- Backups: 20% of server price (optional)
💡 Tip: Start with cx23 for development and scale up as needed. Prices shown are approximate and may vary based on account discounts.
- Traefik Dashboard - View routing rules, SSL certificates, and service health
- Swarmpit - Manage Docker services, view logs, monitor resources
- Dozzle - Real-time container log viewer with search and filtering
All tools are accessible via your configured hostnames with basic authentication.
- Firewall: Only ports 80, 443, and 22 exposed
- SSL/TLS: Automatic certificates via Let's Encrypt
- Authentication: Admin tools protected with bcrypt password hashing
- SSH: Key-based authentication only (password auth disabled)
- Secrets: Never committed to Git (.gitignore configured)
- Validation: Built-in configuration validation before deployment
- Generate unique SSH keys for each environment
- Use strong passwords (12+ characters)
- Rotate credentials regularly
- Enable MFA on Hetzner account
- Review firewall rules periodically
- Keep server software updated
See docs/configuration.md for detailed security recommendations.
Comprehensive documentation is available in the docs/ directory:
- quickstart.md - Get started in 5 minutes
- configuration.md - Complete configuration reference
- deployment.md - Deployment options and workflows
- troubleshooting.md - Common issues and solutions
Configuration validation fails
# Re-run validation with detailed output
./scripts/utils/validate-config.sh terraform/terraform.dev.tfvars
# Check file permissions
chmod 600 terraform/terraform.dev.tfvarsTerraform init fails
cd terraform/
rm -rf .terraform/ .terraform.lock.hcl
terraform initDNS not resolving
# Check DNS records
dig +short yourdomain.com
# Verify Hetzner DNS zone ID
# Login to https://dns.hetzner.com/ and check zone IDSSL certificates not generating
# SSH to server and check Traefik logs
ssh -i ~/.ssh/deploy_ed25519 root@YOUR_SERVER_IP
docker service logs traefik
# Common causes:
# - DNS not propagated (wait 5-10 minutes)
# - Port 80/443 not accessible
# - Invalid email addressServices not accessible
# Check service status
ssh -i ~/.ssh/deploy_ed25519 root@YOUR_SERVER_IP
docker service ls
docker service ps traefik swarmpit dozzle
# View logs
docker service logs <service-name>For more detailed troubleshooting, see docs/troubleshooting.md.
- Update
terraform/versions.tfto use Terraform Cloud:
terraform {
cloud {
organization = "YOUR_ORGANIZATION"
workspaces {
name = "project-dev"
}
}
}- Deploy without
--localflag:
./scripts/deploy-env.sh dev applycd terraform/
# Initialize
terraform init
# Plan with specific var file
terraform plan -var-file="terraform.dev.tfvars"
# Apply changes
terraform apply -var-file="terraform.dev.tfvars"
# View outputs
terraform output
# Destroy infrastructure
terraform destroy -var-file="terraform.dev.tfvars"Manage multiple environments simultaneously:
# Setup all environments
./scripts/setup-env.sh dev
./scripts/setup-env.sh staging
./scripts/setup-env.sh prod
# Deploy all environments
./scripts/deploy-env.sh dev apply --local
./scripts/deploy-env.sh staging apply --local
./scripts/deploy-env.sh prod apply
# View all environment outputs
for env in dev staging prod; do
echo "=== $env ==="
./scripts/deploy-env.sh $env output
doneContributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Dockerswarm.rocks - Docker Swarm best practices
- Hetzner Cloud - Affordable cloud infrastructure
- Traefik - Modern reverse proxy and load balancer
- Swarmpit - Docker Swarm management interface
- Dozzle - Real-time log viewer for Docker
- Documentation: See
docs/directory for detailed guides - Issues: Report bugs or request features via GitHub Issues
- Discussions: Ask questions in GitHub Discussions
Made with ❤️ for the DevOps community