Architecture
How the four pipeline stages connect to produce a running homelab.
Pipeline
graph LR
A[Packer] -->|VM templates| B[Terraform]
B -->|Provisioned VMs| C[Ansible]
C -->|Configured hosts| D[Docker]
| Stage | Tool | What It Does |
|---|---|---|
| 1. Templates | Packer | Builds cloud-init-enabled Ubuntu/Debian images on Proxmox |
| 2. Provisioning | Terraform | Clones templates into VMs with static IPs, CPU, memory, disk |
| 3. Configuration | Ansible | Installs packages, deploys Docker Compose services, configures networking |
| 4. Runtime | Docker Compose | Runs all application containers |
Deployment Order
Infrastructure services have dependencies and must deploy in order:
graph TD
N[Networking] --> CA[Certificate Authority]
N --> NTP[Time Server]
CA --> M[Monitoring]
NTP --> M
M --> Apps[Applications]
task ansible:deploy-networking ENV=wil # 1. DNS, Caddy, Tailscale
task ansible:deploy-ca ENV=wil # 2. Step-CA
task ansible:deploy-ntp ENV=wil # 3. Chrony
task ansible:deploy-monitoring ENV=wil # 4. Prometheus, Grafana, Homepage
task ansible:deploy-media ENV=wil # 5. Applications
Warning
The networking VM must exist before all others — it runs BIND9, which is the nameserver for every other VM.
Repository Structure
ansible/ Playbooks, roles, per-environment inventories
terraform/ VM provisioning with per-environment tfvars
packer/ VM template definitions (Ubuntu, Debian)
docker/ Docker execution environment
tui/ Terminal UI for interactive management
.taskfiles/ Modular Taskfile configs per tool
docs/ This documentation site
Taskfile.yaml Root task runner entry point
Services
Infrastructure
| Service | Host Group | IP (WIL) | Purpose |
|---|---|---|---|
| BIND9, Caddy, Tailscale, DDNS | infra_networking |
10.2.20.53 | DNS, reverse proxy, VPN |
| Step-CA | infra_ca |
10.2.20.9 | Private certificate authority |
| Chrony | infra_ntp |
10.2.20.123 | NTP time synchronization |
| Prometheus, Grafana, Homepage | infra_monitoring |
10.2.20.30 | Metrics and dashboards |
Applications
| Service | Host Group | Purpose |
|---|---|---|
| Media stack (Plex, *arr) | app_mediastack |
Media management and streaming |
| Home Assistant | app_homeassistant |
Home automation |
| Birdle | app_birdle |
Bird identification game |
| Bookstack | app_bookstack |
Wiki/documentation |
| CyberChef | app_cyberchef |
Data encoding/decoding |
| IT-Tools | app_ittools |
Developer utilities |
| Kasm | app_kasm |
Browser-based desktops |
| MicroBin | app_microbin |
Pastebin |
| OpenBooks | app_openbooks |
Book search |
| Restreamer | app_restreamer |
Video streaming relay |
| RomM | app_romm |
ROM manager |
| Seafile | app_seafile |
File sync and share |
| Stirling PDF | app_stirlingpdf |
PDF tools |
| Website | app_website |
Personal website |
| Games Server | app_gamesserver |
Terraria server |
| ConvertX | app_convertx |
File converter |
Key Design Decisions
- Docker Compose over Kubernetes — simpler to operate, sufficient for homelab scale
- Split-horizon DNS with BIND9 — internal clients resolve to internal IPs, external clients to public IP
- SOPS + Age — secrets encrypted at rest, committed to Git, decrypted at deploy time
- Caddy — automatic HTTPS with wildcard certificates via Cloudflare DNS-01
- Task runner — single CLI entry point for all Packer, Terraform, and Ansible operations
- Data-driven app deployment — most apps share a common
deploy-app.ymlplaybook pattern, configured viaapps.yml