Production-ready Kubernetes cluster on Hetzner Cloud with GitOps.
| Component | Spec |
|---|---|
| Control Plane | CX23 (2 vCPU, 4GB) in Falkenstein |
| Workers | 2x CX23 across fsn1 + hel1 |
| Load Balancer | Hetzner LB11 for ingress |
| GitOps | ArgoCD pre-installed |
| CNI | Flannel (via k3s) |
| Ingress | Traefik (k3s default) |
| CCM | Hetzner Cloud Controller Manager |
Cost: ~€17/month
cd terraform
cp terraform.tfvars.example terraform.tfvars
# Edit terraform.tfvars with your Hetzner API token
terraform init
terraform apply# Get kubeconfig
$(terraform output -raw kubeconfig_command)
export KUBECONFIG=$PWD/kubeconfig.yaml
# Verify cluster
kubectl get nodes
# Access ArgoCD UI
kubectl port-forward svc/argocd-server -n argocd 8080:443
# Open https://localhost:8080
# User: admin
# Password: $(terraform output -raw argocd_password_command) Internet
│
┌────────────┴────────────┐
│ Load Balancer │
│ (Hetzner LB11) │
└────────────┬────────────┘
│ :80/:443
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ │
┌───────────────┐ ┌───────────────┐ │
│ Worker 1 │ │ Worker 2 │ │
│ (fsn1) │ │ (hel1) │ │
│ 10.0.1.20 │ │ 10.0.1.21 │ │
└───────────────┘ └───────────────┘ │
│ │ │
└─────────┬─────────┘ │
│ Private Network │
│ (10.0.0.0/16) │
│ │
┌────────┴────────┐ │
│ Control Plane │◄──────────────────┘
│ (fsn1 + ArgoCD) │ :6443 (k8s API)
│ 10.0.1.10 │
└─────────────────┘
- Open ArgoCD UI (see above)
- Click + NEW APP
- Set repo URL, path, and destination
- Click CREATE
kubectl apply -f - <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/YOUR_USERNAME/kubelab
path: k8s/my-app
targetRevision: HEAD
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
EOFkubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80
EOFAccess via load balancer IP from terraform output load_balancer_ip.
| Output | Description |
|---|---|
control_plane_ip |
SSH access to control plane |
worker_ips |
Worker node IPs and locations |
load_balancer_ip |
Public ingress IP |
kubeconfig_command |
Command to fetch kubeconfig |
argocd_password_command |
Get ArgoCD admin password |
argocd_ui_command |
Port-forward command for UI |
terraform destroy- IaC: Terraform + Hetzner Provider
- K8s Distribution: k3s
- GitOps: ArgoCD
- Cloud: Hetzner Cloud (EU regions)