Architecture¶
High-Level Overview¶
The terraform-infra project uses a two-tier module architecture: a root module (main/) that invokes reusable child modules (modules/) to provision infrastructure.
flowchart TB
subgraph "Developer Workflow"
DEV[Developer]
MK[Makefile]
DEV -->|make plan/apply| MK
end
subgraph "Root Module (main/)"
VARS[variables.tf<br/>Input Variables]
MAIN[main.tf<br/>Module Invocation]
BACK[backend.tf<br/>Local State]
OUT[outputs.tf<br/>Cluster Info]
TFVARS[terraform.tfvars<br/>Configuration]
TFVARS --> VARS --> MAIN
MAIN --> OUT
MAIN --> BACK
end
subgraph "Child Modules (modules/)"
KIND[homelab/tf-kind-base<br/>KIND Cluster Module]
AWS[aws/<br/>Future AWS Modules]
end
subgraph "Infrastructure"
CLUSTER[KIND Cluster<br/>1 CP + 2 Workers]
KUBE[~/.kube/config<br/>Kubeconfig]
end
MK -->|tofu/terraform| MAIN
MAIN -->|module source| KIND
KIND -->|Provisions| CLUSTER
KIND -->|Configures| KUBE
Module Composition¶
The project follows a root-module-calls-child-module pattern, keeping the root module thin and delegating all resource creation to reusable modules.
flowchart LR
subgraph "main/"
ROOT[main.tf]
end
subgraph "modules/homelab/"
KIND_MOD[tf-kind-base/]
end
subgraph "modules/aws/"
AWS_MOD[Future Modules]
end
ROOT -->|"module \"homelab-kind\""| KIND_MOD
ROOT -.->|"module \"aws-*\" (future)"| AWS_MOD
Root Module (main/)¶
The root module serves as the entry point and configuration layer:
| File | Responsibility |
|---|---|
main.tf |
Invokes homelab/tf-kind-base module with variables |
variables.tf |
Defines all input variables with types and descriptions |
outputs.tf |
Exposes cluster name, endpoint, kubeconfig, context, and Flux bootstrap command |
backend.tf |
Configures local state backend |
versions.tf |
Pins tehcyx/kind provider to ~> 0.4.0 |
Child Module (homelab/tf-kind-base)¶
The KIND base module handles all cluster resource creation:
- Constructs the node image from version input (
kindest/node:v1.31.0) - Creates a 3-node KIND cluster (1 control-plane + 2 workers)
- Configures port mappings for Traefik ingress (80/443)
- Applies node labels for workload scheduling
- Merges common tags with cluster-specific metadata
- Outputs cluster connection details and certificates
Multi-Environment Strategy¶
The project supports multiple environments through per-environment variable files:
flowchart TB
subgraph "Variable Files (tfvars/)"
DEV_VARS[dev.tfvars<br/>env = dev]
UAT_VARS[uat.tfvars<br/>env = uat]
PROD_VARS[prod.tfvars<br/>env = prod]
end
subgraph "Root Module"
MAIN[main.tf]
end
subgraph "Clusters"
DEV_K8S[Dev Cluster<br/>dev-services-amer]
UAT_K8S[UAT Cluster]
PROD_K8S[Prod Cluster]
end
DEV_VARS -->|"tofu apply -var-file"| MAIN --> DEV_K8S
UAT_VARS -->|"tofu apply -var-file"| MAIN --> UAT_K8S
PROD_VARS -->|"tofu apply -var-file"| MAIN --> PROD_K8S
| Environment | Variable File | Cluster Name | Kubernetes Version |
|---|---|---|---|
| Development | tfvars/dev.tfvars |
dev-services-amer |
v1.31.0 |
| UAT | tfvars/uat.tfvars |
Configurable | v1.31.0 |
| Production | tfvars/prod.tfvars |
Configurable | v1.31.0 |
Alternative: OpenTofu/Terraform workspaces can also be used for environment isolation:
State Management¶
flowchart LR
TF[OpenTofu/Terraform] -->|Read/Write| STATE[terraform.tfstate<br/>Local File]
STATE -->|Contains| RESOURCES[Cluster State<br/>+ Outputs]
| Setting | Value |
|---|---|
| Backend | Local (terraform.tfstate in main/) |
| Locking | File-system level (single user) |
| State File | Gitignored (.tfstate in .gitignore) |
Local State by Design
Local state is intentional for a personal homelab. For team collaboration, the backend can be switched to S3 + DynamoDB with a simple backend.tf change.
Provider Architecture¶
flowchart LR
TF[OpenTofu/Terraform<br/>>= 1.0]
KIND_PROV[tehcyx/kind Provider<br/>~> 0.4.0]
DOCKER[Docker API<br/>Colima / Docker Desktop]
K8S[KIND Cluster<br/>Kubernetes v1.31.0]
TF -->|Uses| KIND_PROV
KIND_PROV -->|Calls| DOCKER
DOCKER -->|Creates| K8S
| Component | Version Constraint | Purpose |
|---|---|---|
| OpenTofu/Terraform | >= 1.0 |
IaC execution engine |
| tehcyx/kind provider | ~> 0.4.0 |
KIND cluster resource management |
| KIND | Latest | Kubernetes-in-Docker cluster tool |
| Docker | Required | Container runtime for KIND nodes |
Cluster Architecture¶
flowchart TB
subgraph "Docker (Colima)"
subgraph "KIND Cluster: dev-services-amer"
CP["Control Plane Node<br/>Role: control-plane<br/>Ports: 80→30080, 443→30443"]
W1["Worker Node 1<br/>Role: worker"]
W2["Worker Node 2<br/>Role: worker"]
end
end
subgraph "Host Machine"
KUBE[kubeconfig<br/>~/.kube/config]
HTTP["localhost:80<br/>HTTP Traffic"]
HTTPS["localhost:443<br/>HTTPS Traffic"]
end
CP <-->|API| KUBE
HTTP -->|NodePort 30080| CP
HTTPS -->|NodePort 30443| CP
CP -->|Schedules| W1 & W2
Design Decisions¶
Module-Based vs Monolithic
Separating the KIND cluster into a reusable module (modules/homelab/tf-kind-base) enables:
- Reusability across different root configurations
- Testability of the module in isolation
- Extensibility via the
modules/aws/placeholder for future cloud resources
Why KIND over Minikube?
- Multi-node clusters (production-like topology)
- Native Docker integration (no VM overhead)
- Configurable port mappings for ingress
- Faster cluster creation (~2-3 minutes)
- Better FluxCD compatibility
OpenTofu over Terraform
The Makefile auto-detects tofu or terraform, preferring OpenTofu:
- Open-source fork with community governance
- Fully compatible with existing Terraform providers
- No licensing restrictions for commercial use
Local State vs Remote State
Local state is chosen for simplicity in a single-developer homelab:
- No S3 bucket or DynamoDB table to manage
- No credential configuration required
- State file is gitignored for safety
- Easily upgradeable to remote backend when needed
Directory Layout Philosophy¶
terraform-infra/
├── main/ # WHAT to deploy (configuration + variables)
├── modules/ # HOW to deploy (reusable resource definitions)
│ ├── homelab/ # Local/homelab infrastructure
│ └── aws/ # Cloud infrastructure (future)
└── scripts/ # Automation helpers (future)
| Directory | Analogy | Changes When |
|---|---|---|
main/ |
"The order" | Environment config changes |
modules/ |
"The recipe" | Infrastructure patterns change |
scripts/ |
"The shortcuts" | Workflow automation changes |