Problema
En muchos homelabs la lista de contenedores crece de forma orgánica: se añaden servicios copiando archivos docker‑compose.yml antiguos, se asignan puertos a mano y se crean volúmenes sin una política clara. Con el tiempo aparecen colisiones de puertos, variables de entorno olvidadas y health checks ausentes. Cada error obliga a volver a la máquina, revisar logs y, a menudo, a reiniciar manualmente los stacks. El patrón es claro: la configuración declarativa no está versionada ni validada antes de llegar al host, lo que genera inconsistencias y tiempo de inactividad evitables.
Causa
- Ausencia de GitOps – Los archivos de Compose viven solo en el servidor. Sin un repositorio que sirva como única fuente de verdad, cualquier cambio local se pierde o se duplica.
- Falta de validación estática – No hay linting ni pruebas de esquema que detecten puertos duplicados, variables sin valor o configuraciones inseguras antes del despliegue.
- Despliegues manuales – Ejecutar
docker compose up -ddirectamente en el host evita cualquier control de calidad y dificulta la reversión. - Gestión de secretos ad‑hoc – Variables sensibles se inyectan mediante archivos
.envsin control de acceso, lo que aumenta el riesgo de exposición. - Visibilidad limitada – Sin una herramienta que pueda inspeccionar contenedores en tiempo real, la detección de fallos depende de la intuición del administrador.
Solución
Implementar un flujo GitOps basado en Docker Compose, Gitea (o cualquier servidor Git con CI) y GitHub/Gitea Actions. El repositorio contiene:
- Plantillas de Compose con anclajes (
x-common-keys,x-common-logging) que garantizan opciones de seguridad, logging y restart policy. - Un archivo
allocated-ports.mdque registra cada puerto expuesto. Un script de CI compara eldocker compose configrenderizado con este inventario y falla si hay colisiones. - Un workflow de Actions que:
- Genera un
.envtemporal a partir de los secretos almacenados en Gitea. - Ejecuta
docker compose configpara validar sintaxis y aplicar los anclajes. - Corre Trivy contra Dockerfiles y contra imágenes resultantes.
- Lanza
docker compose up -d --remove-orphans. - Espera a que todos los health checks reporten
healthy. Si alguno falla, captura los últimos logs y ejecuta una rollback basada en los IDs de imagen previos.
- Genera un
- Un runner con acceso read‑write al socket Docker para despliegues y un proxy read‑only para herramientas de monitoreo.
- Opcionalmente, un contenedor como OpenChamber que expone la CLI de Docker y Gitea a través de una UI web, permitiendo que un modelo de IA (por ejemplo, un LLM local) genere o corrija archivos YAML, inspeccione logs y dispare workflows desde cualquier dispositivo.
Paso a paso resumido
- Estructura del repositorio
MyServer/ docker/ <stack>/ docker-compose.yml Dockerfile config/ .gitea/ actions/ deploy-stack/ main.yml - Plantilla de Compose – Incluye anclajes comunes y health checks obligatorios.
- Script de validación (
dev validate) que ejecuta:docker compose config- Linter de Compose (chequea
no-new-privileges:true,restart,logging,healthcheck). - Comparación de puertos contra
allocated-ports.md.
- Workflow de despliegue – Usa los pasos descritos arriba.
- Rollback automático – Guarda los IDs de imagen antes del
docker compose up; si la fase de health check falla, re‑etiqueta y recrea los contenedores con esas imágenes.
Cuándo aplicar esta solución
- Homelabs con más de 5 stacks donde la gestión manual ya genera colisiones de puertos o variables perdidas.
- Entornos que requieren alta disponibilidad de servicios críticos (Home Assistant, DNS, reverse proxy).
- Equipos que usan IA para generar o validar configuraciones; el flujo GitOps provee una barrera de seguridad antes de que la IA aplique cambios.
- No es necesario si solo se ejecuta un único contenedor estático y no hay necesidad de versionado o rollback.
Código
# dev script – validación básica
#!/usr/bin/env bash
set -euo pipefail
# Render Compose y captura salida
rendered=$(docker compose -f "$1/docker-compose.yml" config)
# Chequeo de puertos duplicados
awk '/ports:/ {gsub(/[-"]/,"",$2); print $2}' <<<"$rendered" | \
sort | uniq -d | if read dup; then
echo "Puerto duplicado detectado: $dup"
exit 1
fi
# Linter mínimo (ejemplo)
if ! grep -q "no-new-privileges:true" <<<"$rendered"; then
echo "Falta no-new-privileges en al menos un servicio"
exit 1
fi
echo "Validación exitosa"
Verificación
- Push a
main– Gitea ejecuta el workflow. - Revisa la página de Actions – Debe pasar todas las etapas: render, lint, Trivy, despliegue, health check.
- Comprueba DNS – Si el workflow registra el dominio en AdGuard, verifica que
dig <host>.homedevuelve la IP esperada. - Prueba de rollback – Induce un fallo (por ejemplo, cambia la etiqueta de la imagen a una inexistente) y confirma que los contenedores vuelven a la versión anterior y que los logs aparecen en la salida del job.
Notas adicionales
- Límites de redes Docker – Cuando se usan muchos stacks, aumenta el
default-address-poolen/etc/docker/daemon.jsonpara evitar el error could not find an available, non-overlapping IPv4 address pool. - Separación de sockets – Usa
docker-socket-proxypara limitar el acceso adocker.sock. Los runners de CI necesitan permisos de escritura; las herramientas de monitoreo pueden usar una instancia read‑only. - Gestión de secretos – Nunca almacenes valores sensibles en archivos
.envversionados. Gitea Actions permite inyectar secretos directamente en el proceso dedocker compose. - AI como asistente, no como autoridad – Deja que el modelo genere cambios, pero siempre pasa por el pipeline de validación antes de aplicar. Esto evita que un error de interpretación cause downtime.
- Backup de volúmenes – Configura un contenedor como Duplicati o Restic que respalde
${DOCKERDIR}periódicamente. La restauración de datos es independiente del proceso de despliegue.