Gitea runner OpenTofu backend contract
This directory defines the safe backend, provider contract, and kube-hetzner cluster stack for the Gitea runner Kubernetes cluster.
Required backend
Production state must use the OpenTofu S3 backend in backend.tf:
- bucket:
gitea-runner-hectic-lab - key:
gitea-runners/kube-hetzner/terraform.tfstate - region:
fsn1, aligned with the target Hetzner location - encryption:
encrypt = true - locking:
use_lockfile = truewhere the selected S3-compatible endpoint supports it
Before any production tofu init, verify the S3-compatible endpoint, credential
source, bucket versioning, encryption behavior, and lockfile support for the
chosen object-storage provider. Keep backend authentication externalized through
environment variables, AWS-compatible shared config, or the production secret
injection path from Task 3. Do not add access_key, secret_key, Hetzner
tokens, runner tokens, kubeconfig material, or decrypted SOPS data to checked-in
OpenTofu files.
Local state safety
Production local state is forbidden. Only syntax-only validation/prototyping may use local state, and it must use backend-disabled initialization:
tofu -chdir=infra/gitea-runners/opentofu init -backend=false
tofu -chdir=infra/gitea-runners/opentofu validate
Fail the run if production local state appears:
test ! -e infra/gitea-runners/opentofu/terraform.tfstate
test ! -e infra/gitea-runners/opentofu/terraform.tfstate.backup
grep -R 'backend "s3"' infra/gitea-runners/opentofu
The .gitignore in this directory blocks local state, plans, downloaded
providers/modules, and variable files from being committed. Treat any local
state file as disposable validation residue, never as production state.
Provider and module pins
versions.tf pins the OpenTofu-compatible Hetzner Cloud provider to
hetznercloud/hcloud version 1.60.1. kube-hetzner research for this plan
observed module version 2.19.3, source kube-hetzner/kube-hetzner/hcloud, and
module minimum hcloud provider requirement >= 1.59.0; these values are recorded
as locals so Task 5 can wire the module without re-opening the version contract.
providers.tf leaves the hcloud provider empty so authentication comes from
the provider's external environment/config mechanisms such as HCLOUD_TOKEN.
Do not set token values in .tf or .tfvars files.
Cluster shape
The default cluster is deliberately fixed-size:
- cluster name:
gitea-runners - Hetzner location:
fsn1 - private network region:
eu-central - control plane: one
cpx21node in poolcontrol-plane - workers: three
cpx31nodes in poolrunner-workers - storage: Hetzner CSI enabled with expected StorageClass
hcloud-volumes - Longhorn: disabled
- autoscaling/KEDA: not enabled in this stack
The three default workers are sized for the initial five trusted privileged DinD
jobs. To scale toward ten jobs later, keep autoscaling disabled and either raise
worker_count to 5 or increase worker_server_type, then run a fresh
tofu plan and the Task 11 Kubernetes pressure checks before applying.
Required inputs must come from environment or secret injection, for example
TF_VAR_hcloud_token, TF_VAR_ssh_public_key, and TF_VAR_ssh_private_key.
Do not commit .tfvars files. kube-hetzner v2.19.3 writes the generated
kubeconfig to ./<cluster_name>_kubeconfig.yaml when create_kubeconfig is
enabled; this path is ignored as operational secret material.
Known state caveat
kube-hetzner may thread hcloud_token into Kubernetes secrets/state through its
internal kube_system_secrets handling. This task does not claim that risk is
solved. Task 5 must verify the generated plan and state before production apply
and prove that Hetzner tokens, S3 credentials, runner tokens, kubeconfig private
keys, and decrypted secrets are absent from committed files and unsafe state
evidence.