Problema
Mantener un homelab en casa implica crear máquinas virtuales, instalar servicios y actualizar configuraciones de forma manual. Cada vez que se añade un nuevo contenedor o se cambia una versión de Ubuntu, el proceso se vuelve repetitivo y propenso a errores. El patrón típico es:
- Provisión de VMs en Proxmox (a menudo con cloud‑init).
- Instalación de Docker y despliegue de stacks mediante
docker-compose.yml. - Configuración de accesos externos (por ejemplo, Cloudflare Tunnel).
Sin una capa de automatización, reproducir el entorno en otro nodo o restaurarlo después de un fallo requiere volver a ejecutar cada paso a mano. Además, la gestión de secretos y la separación entre entornos de desarrollo y producción suele quedar a medias, lo que complica la evolución del laboratorio.
Causa
Los principales factores que generan esta fricción son:
- Ausencia de definición declarativa: los recursos se crean con clicks en la UI de Proxmox o con scripts ad‑hoc, lo que impide versionar la infraestructura.
- Descoordinación entre IaC y despliegue de contenedores: Terraform puede crear la VM, pero el stack Docker se lanza manualmente, rompiendo la cadena de entrega continua.
- Gestión de secretos dispersa: claves de API, tokens de Cloudflare y credenciales de bases de datos se guardan en archivos locales sin cifrado ni control de acceso.
- Flujos de CI/CD poco estructurados: algunos usuarios usan solo “push → deploy” sin revisiones, mientras que otros requieren aprobaciones manuales, lo que lleva a decisiones inconsistentes.
Estas causas aparecen tanto en entornos de producción ligera como en laboratorios personales, y la solución debe ser lo suficientemente genérica para adaptarse a ambos.
Solución
Una arquitectura basada en tres capas permite cubrir todo el ciclo de vida:
- IaC con Terraform/OpenTofu para describir la infraestructura de Proxmox (hosts, redes, discos).
- Ansible para configurar el SO de la VM (instalar Docker, crear usuarios, aplicar hardening) y para desplegar los archivos
docker‑compose.yml. - Pipeline CI/CD (GitHub Actions, Gitea Actions o similar) que orquesta el flujo:
- Detecta cambios en el repositorio.
- Ejecuta
terraform applyen modo plan‑only para validar. - Si la validación pasa, dispara Ansible contra la VM objetivo.
- Opcionalmente, solicita aprobación manual antes del despliegue a producción.
Herramientas recomendadas
| Capa | Herramienta | Por qué |
|---|---|---|
| IaC | Terraform (o OpenTofu) | Provider oficial para Proxmox, estado remoto fácil de almacenar en Git. |
| Config | Ansible | Idempotente, sin agente, excelente para gestionar Docker y archivos de composición. |
| CI/CD | GitHub Actions (o Gitea Actions) | Integración nativa con repositorios, permite secret management con SOPS. |
| Secrets | SOPS + Git‑crypt o HashiCorp Vault (modo dev) | Cifrado de archivos YAML/JSON, integración directa con GitHub Secrets. |
Flujo típico
- Repositorio Git contiene:
terraform/con módulos Proxmox.ansible/con playbooks y plantillasdocker-compose.yml.sops/con archivos cifrados de secretos.
- Push a rama
maindispara el workflow. - Job “plan” ejecuta
terraform plany publica el plan como artefacto. - Job “approve” (opcional) espera aprobación manual.
- Job “apply” corre
terraform apply -auto-approve. - Job “configure” llama a Ansible:
ansible-playbook -i inventory.yml site.yml. - Job “notify” envía mensaje a Discord/Telegram con el resumen del despliegue.
Este enfoque mantiene todo bajo control de versiones, permite reconstruir el homelab en minutos y brinda una pista de auditoría clara.
Cuándo aplicar esta solución
Ideal cuando:
- Se gestionan varias VMs en Proxmox y se desea reproducir el entorno en otro nodo.
- Los stacks Docker cambian con frecuencia (pruebas de nuevas versiones, experimentos).
- Se quiere practicar procesos de CI/CD que luego puedan trasladarse a entornos profesionales.
No recomendable si:
- El laboratorio consta de una única VM y el overhead de Terraform/Ansible supera el beneficio.
- No se dispone de un repositorio remoto o de un servidor de CI que pueda ejecutar los jobs.
En esos casos, scripts bash simples pueden ser suficientes, pero perderás la capacidad de versionar y escalar.
Código
# terraform/main.tf – ejemplo de recurso Proxmox VM
terraform {
required_providers {
proxmox = {
source = "Telmate/proxmox"
version = "~> 2.9"
}
}
backend "local" {}
}
provider "proxmox" {
pm_api_url = var.proxmox_url
pm_user = var.proxmox_user
pm_password = var.proxmox_password
pm_tls_insecure = true
}
resource "proxmox_vm_qemu" "ubuntu_vm" {
name = "homelab-${var.vm_name}"
target_node = var.proxmox_node
iso = "local:iso/ubuntu-22.04-server-cloudimg-amd64.img"
cores = 2
memory = 4096
scsihw = "virtio-scsi-pci"
bootdisk = "scsi0"
disk {
slot = 0
size = "20G"
type = "scsi"
storage = "local-lvm"
}
network {
model = "virtio"
bridge = "vmbr0"
}
cloudinit {
user = "ubuntu"
password = var.vm_password
sshkeys = file(var.ssh_pubkey_path)
}
}
# ansible/playbook.yml – instalación de Docker y despliegue Compose
- hosts: homelab
become: true
vars_files:
- ../sops/secrets.yml # cifrado con SOPS
tasks:
- name: Instalar paquetes requeridos
apt:
name: [docker.io, docker-compose]
state: present
update_cache: true
- name: Copiar docker‑compose.yml
copy:
src: ../docker-compose.yml
dest: /opt/app/docker-compose.yml
owner: root
mode: '0644'
- name: Levantar stack
command: docker compose -f /opt/app/docker-compose.yml up -d
args:
chdir: /opt/app
# .github/workflows/homelab.yml – pipeline CI/CD
name: Homelab Deploy
on:
push:
branches: [main]
jobs:
plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Terraform Init & Plan
run: |
terraform -chdir=terraform init
terraform -chdir=terraform plan -out=plan.out
- name: Upload plan
uses: actions/upload-artifact@v4
with:
name: tfplan
path: terraform/plan.out
apply:
needs: plan
if: github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Download plan
uses: actions/download-artifact@v4
with:
name: tfplan
path: terraform
- name: Terraform Apply
run: terraform -chdir=terraform apply -auto-approve plan.out
configure:
needs: apply
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Ansible
run: sudo apt-get update && sudo apt-get install -y ansible
- name: Run Ansible Playbook
env:
ANSIBLE_HOST_KEY_CHECKING: false
run: ansible-playbook -i ansible/inventory.yml ansible/playbook.yml
Verificación
- Estado de Terraform:
terraform -chdir=terraform showdebe listar la VM con los atributos esperados. - Conexión SSH:
ssh ubuntu@<ip_vm>debe aceptar la clave pública definida en cloud‑init. - Docker Compose:
docker psdentro de la VM debe mostrar los contenedores declarados endocker-compose.yml. - Pipeline: En la página de Actions, cada job debe terminar con “Success”. Un mensaje en Discord confirma la finalización.
Notas adicionales
- Persistencia del estado: aunque el backend local funciona, migrar a un bucket S3 o a GitHub Encrypted Secrets evita colisiones cuando varios usuarios editan la infraestructura.
- Versionado de imágenes: usa etiquetas fijas (
myapp:1.2.3) en los archivos Compose para que los despliegues sean reproducibles. - Rollback rápido: guarda la salida de
terraform plancomo artefacto; si algo falla, ejecutaterraform apply -destroycon el mismo plan para volver al estado previo. - Secretos en Ansible: SOPS permite cifrar
secrets.ymlcon tu GPG key. En GitHub Actions, agrega la clave privada como secret y descifra en tiempo de ejecución. - Escalado a Kubernetes: cuando el laboratorio crezca, sustituye el playbook de Docker Compose por un manifiesto Helm y conecta Argo CD al mismo repositorio; la transición es casi transparente porque la capa de IaC sigue siendo Terraform.