Problema
Mantener un homelab pequeño pero funcional suele colapsar cuando la configuración se vuelve manual. Cada vez que se añade una nueva VM, un contenedor LXC o un servicio (por ejemplo, AdGuard o un clúster k3s), el proceso implica varios pasos repetitivos: crear discos, asignar redes, instalar paquetes y ajustar configuraciones de arranque. Con el tiempo, la documentación se dispersa, los cambios se pierden y la recuperación tras un fallo de hardware se vuelve un dolor de cabeza. El desafío es conseguir que la infraestructura sea reproducible, versionable y desplegable con un solo comando, sin depender de ajustes manuales en cada nodo.
Causa
- Ausencia de IaC (Infrastructure as Code). Sin una capa declarativa, los recursos se crean de forma ad‑hoc y el estado real del entorno no se refleja en ningún repositorio.
- Separación de herramientas. Usar Terraform para la provisión de VMs y Ansible para la configuración es una práctica recomendada, pero cuando cada herramienta tiene su propio flujo de trabajo, el proceso se vuelve fragmentado.
- Falta de orquestador de alto nivel. Ejecutar varios scripts por separado aumenta la probabilidad de errores humanos y dificulta la automatización completa.
- Gestión de secretos y estado dispersa. Guardar credenciales en archivos locales o en el código fuente expone el entorno a riesgos y complica la migración a otro nodo.
- Redes caseras sin DHCP centralizado. Cuando el único servidor que ofrece DNS/DHCP cae, toda la red pierde conectividad, lo que evidencia la necesidad de alta disponibilidad o al menos de una estrategia de fallback.
Solución
Una arquitectura basada en Terraform + Ansible + Make permite describir la infraestructura, aplicar configuraciones y encadenar los pasos en una única orden (make all). El flujo típico es:
- Terraform declara los recursos de Proxmox (VMs, contenedores LXC, discos, interfaces de red). Usa el provider oficial de Proxmox y cloud‑init para inyectar usuarios y claves SSH.
- Ansible consume el inventario generado por Terraform y ejecuta playbooks que instalan Docker, despliegan AdGuard, configuran k3s o cualquier otro servicio.
- Make actúa como “orquestador de comandos”, definiendo targets (
plan,apply,provision,deploy) que llaman a Terraform y Ansible en el orden correcto. Con variables de entorno se pueden pasar secretos sin escribirlos en disco. - Vault o SOPS protege los archivos
.tfvarsy losansible_vault.yml. El estado de Terraform se guarda en un backend remoto (por ejemplo, HashiCorp Cloud) para que cualquier máquina pueda continuar el trabajo. - Redundancia ligera: se configura una segunda VM mínima que actúe como DNS/DHCP de reserva usando
dnsmasq. En caso de caída del nodo principal, el router puede apuntar al backup mediante una regla estática.
Paso a paso resumido
- Inicializar Terraform
terraform init -backend-config="bucket=homelab-tfstate" - Planificar cambios
terraform plan -var-file=secrets.tfvars - Aplicar infraestructura
terraform apply -auto-approve -var-file=secrets.tfvars - Generar inventario dinámico
Terraform exporta un archivoinventory.inicon IPs y nombres de host. - Ejecutar Ansible
ansible-playbook -i inventory.ini site.yml - Orquestar todo con Make
make all
Cuándo aplicar esta solución
- Escala pequeña‑media (1‑5 nodos) donde la complejidad de la red justifica IaC pero no se necesita un orquestador de clúster completo.
- Entornos de pruebas que deben ser recreados frecuentemente (p.ej., para validar actualizaciones de k3s o de contenedores).
- Equipos con conocimientos básicos de Terraform y Ansible; la curva de aprendizaje se amortiza rápidamente al ganar reproducibilidad.
- Situaciones donde el tiempo de inactividad es crítico: al tener el estado en la nube y los playbooks versionados, una reinstalación del hardware se reduce a “clonar el repo y ejecutar
make all”.
No es adecuado cuando:
- Se requiere alta disponibilidad a nivel de hipervisor (cluster Proxmox con Ceph, etc.).
- La infraestructura supera varios decenas de nodos; en ese caso conviene migrar a herramientas como Pulumi o Terraform Cloud con workspaces más avanzados.
- No se dispone de una máquina de gestión que pueda ejecutar Make/Ansible de forma continua.
Código
# Makefile (fragmento esencial)
.PHONY: all plan apply provision deploy
all: plan apply provision
plan:
terraform plan -var-file=secrets.tfvars
apply:
terraform apply -auto-approve -var-file=secrets.tfvars
provision:
ansible-playbook -i inventory.ini site.yml
deploy:
# Aquí irían los playbooks de apps (k3s, etc.)
@echo "Despliegue de aplicaciones pendiente"
# terraform/main.tf (ejemplo de VM con cloud‑init)
provider "proxmox" {
endpoint = var.proxmox_endpoint
api_token_id = var.api_token_id
api_token_secret = var.api_token_secret
insecure_skip_verify = true
}
resource "proxmox_vm_qemu" "adguard" {
name = "adguard"
target_node = "pve"
vmid = 101
memory = 2048
cores = 2
scsihw = "virtio-scsi-pci"
bootdisk = "scsi0"
disk {
slot = 0
size = "20G"
type = "scsi"
storage = "local-lvm"
}
network {
model = "virtio"
bridge = "vmbr0"
}
cloudinit {
user = "admin"
password = var.vm_password
ssh_keys = file(var.ssh_pub_key)
}
}
# ansible/playbooks/site.yml (snippet)
- hosts: all
become: true
vars_files:
- vault.yml
tasks:
- name: Instalar paquetes base
apt:
name: [docker.io, curl, gnupg2]
state: present
update_cache: yes
- name: Desplegar AdGuard Home (Docker)
docker_container:
name: adguard
image: adguard/adguardhome:latest
restart_policy: unless-stopped
ports:
- "53:53/tcp"
- "53:53/udp"
- "80:80/tcp"
- "443:443/tcp"
volumes:
- "/opt/adguard/work:/opt/adguard/work"
- "/opt/adguard/conf:/opt/adguard/conf"
Verificación
-
Estado de Terraform
terraform showConfirma que los recursos aparecen con los IDs esperados y que las IP asignadas coinciden con el inventario.
-
Conectividad de Ansible
ansible -m ping all -i inventory.iniTodos los hosts deben responder
pong. -
Servicios críticos
- Acceder a
http://<IP_ADGUARD>:80y comprobar la UI de AdGuard. - Ejecutar
docker psdentro de la VM para validar que el contenedor está corriendo. - Si se configuró k3s,
kubectl get nodesdebe listar el nodo.
- Acceder a
-
Resiliencia de red
- Apagar la VM principal y verificar que el backup DNS/DHCP responde a consultas (
dig @<IP_BACKUP> example.com).
- Apagar la VM principal y verificar que el backup DNS/DHCP responde a consultas (
Notas adicionales
- Manejo de secretos: Usa
ansible-vaultpara contraseñas ysopspara cifrar los.tfvars. Evita variables de entorno en texto plano. - Idempotencia: Tanto Terraform como Ansible son declarativos; sin embargo, revisa que los playbooks no incluyan tareas que dependan de estado externo (p.ej.,
shell: docker pull …sincreates). - Versionado: Mantén el repositorio en Git con ramas
devyprod. Los cambios enmainpueden disparar pipelines CI que ejecutenterraform fmtyansible-lint. - Backup del estado: Si el backend remoto falla, Terraform puede restaurarse desde un snapshot del bucket de almacenamiento. Configura rotación de snapshots.
- Escalado futuro: Cuando necesites añadir un nodo Proxmox, simplemente extiende el bloque
proxmox_vm_qemuo crea un módulo reutilizable. El resto del flujo (make all) sigue siendo el mismo.
Con este enfoque, cualquier homelab basado en Proxmox pasa de ser una colección de máquinas configuradas a mano a una plataforma reproducible, versionable y fácil de mantener. La inversión inicial en IaC paga rápidamente en tiempo de recuperación, consistencia y capacidad de experimentar sin temor a romper el entorno.