Docker Compose en homelab: organización, perfiles y buenas prácticas

Última actualización: 24 de mayo de 2026
  • Organizar Docker Compose por perfiles y funciones simplifica la gestión de homelabs con decenas de servicios.
  • Centralizar configuración en .env, usar overrides y versionar en Git hace el entorno portable y fácil de migrar.
  • Redes dedicadas, Traefik y healthchecks mejoran seguridad, aislamiento y resiliencia de los servicios.
  • Monitorización, logs controlados y backups automatizados convierten el homelab en una plataforma estable a largo plazo.

docker compose homelab

Montar un homelab moderno con contenedores se ha convertido en el hobby favorito de mucha gente tech. Docker Compose es casi siempre el corazón de ese montaje: define tus servicios en YAML, los versionas con Git y puedes levantar todo tu entorno con un solo comando.

Ahora bien, cuando empiezas a crecer pasan cosas: pasas de tener dos o tres contenedores a decenas de servicios, redes internas, proxys inversos, bases de datos y runners de CI. Ahí es cuando surge la duda gorda: ¿un único docker-compose gigante o muchos ficheros pequeños? ¿cómo organizo perfiles, redes, backups, seguridad, y encima lo hago fácil de migrar?

Enfoques reales para organizar Docker Compose en un homelab

configuración homelab con docker compose

En la práctica, la gente que lleva tiempo con homelabs suele moverse en tres modelos de organización de Compose, cada uno con sus ventajas e inconvenientes. Elegir bien el enfoque te ahorra mucho dolor al crecer o migrar de máquina.

Por un lado está quien comenzó con docker run sueltos, luego pasó a Portainer y acabó saltando a Docker Compose. Es lo típico: Portainer da mucha visibilidad, interfaz cómoda, plantillas, etc., pero al final editar parámetros complejos o migrar configuraciones se vuelve un lío si no tienes nada en ficheros.

En el extremo opuesto está quien ha consolidado todo en un único “mega” docker-compose.yml capaz de levantar absolutamente todos los servicios del homelab: proxy inverso, media, utilidades, monitorización, LLMs, bases de datos… Todo en un solo stack.

Entre medias, muchos usuarios se quedan con un enfoque mixto: varios docker-compose.yml pequeños agrupados por contexto (por ejemplo, media, infraestructura, productividad, monitorización), todos bajo un mismo repositorio y normalmente compartiendo variables de entorno globales.

Una solución bastante elegante mezcla ambos mundos: un docker-compose “raíz” que hace include de otros ficheros (cada uno en una subcarpeta de apps o services). Así mantienes una visión global del homelab, pero sin sufrir un YAML de mil líneas imposible de leer.

Perfiles, agrupación por funciones y homelabs de gran tamaño

perfiles docker compose homelab

Cuando tu homelab empieza a rozar los 30, 40 o 50 servicios (incluyendo servicios de respaldo como bases de datos, cachés o indexadores) es vital poner orden. Aquí entran en juego tanto la agrupación por función como el uso de perfiles de Docker Compose.

Un patrón muy habitual es agrupar todo en un único “proyecto” Compose, pero dividido lógicamente por perfiles. Por ejemplo:

  • Perfil core: núcleo del homelab, con Traefik como reverse proxy y un proveedor de identidad (por ejemplo, OAuth o Authentik) para autenticar todas las apps bajo un mismo dominio con HTTPS.
  • Perfil media: servicios estilo Plex, Sonarr, Radarr, Ombi, SABnzbd o qBittorrent, responsables de curar, descargar y servir contenido multimedia.
  • Perfil utilities: herramientas como Portainer, Watchtower (si se usa), Diun, dockcheck o similares para gestionar y vigilar contenedores y actualizaciones.
  • Perfil infra/monitoring: Traefik, cAdvisor, Prometheus, Grafana, Uptime Kuma, Dozzle y todo lo relativo a monitorización y logging.
  • Perfiles experimentales o LLM: pilas específicas para LLMs o apps curiosas (ChatGPT Next Web local, LibreOffice Online, etc.) que suelen venir desactivadas por defecto.

La gracia de los perfiles es que puedes lanzar solo una parte de la infraestructura según te interese. Por ejemplo, ejecutar solo el perfil core + infra en un mini PC de baja potencia, y levantar el perfil media únicamente en el servidor grande con más discos y GPU.

En repositorios bien pensados, suele haber en la raíz un docker-compose.yml “maestro” que tira de include hacia ficheros individuales en una carpeta apps/ o services/. Además, casi todos los servicios se configuran vía un único fichero .env global y algunos secretos en un directorio secrets/, lo que simplifica mucho la configuración inicial.

Siguiendo este patrón, la gestión del homelab se reduce básicamente a editar el .env y los secretos, activar o desactivar perfiles y decidir qué servicios arrancas en cada host. Ideal si vas a desplegar el mismo conjunto de aplicaciones en varios equipos.

Un solo docker-compose gigante vs varios ficheros pequeños

estructura de ficheros docker compose homelab

Esta es la discusión eterna: ¿un único docker-compose.yml con todo dentro o varios ficheros por servicio/stack? La respuesta real suele ser “depende de lo que quieras priorizar: simplicidad de migración o claridad por servicio”.

Quien defiende el fichero maestro único suele destacar varias ventajas:

  • Migrar de host es facilísimo: clonas el repositorio, copias el .env y los secretos, montas los volúmenes y haces un docker compose up -d. No hay que ir directorio por directorio.
  • Infraestructura como código de verdad: toda la topología del homelab (servicios, redes, volúmenes, dependencias) está en un solo sitio.
  • Actualizaciones centralizadas: cambias una versión de imagen, una política de reinicio o algo de logging y sabes exactamente dónde tocar.
  Qué es la carpeta WOW64, SysWOW64 y para qué sirve en Windows

Pero también tiene pegas claras: un YAML enorme es más difícil de mantener, los conflictos de merges aumentan y a la hora de depurar un problema concreto te ves navegando un monstruo de cientos de líneas. No es raro arrepentirse un poco cuando todo crece demasiado.

El otro enfoque es tener un docker-compose.yml por app o por stack lógico, dentro de una estructura tipo:

<code>docker/
├── bookstack/
│   └── docker-compose.yml
├── dashy/
│   └── docker-compose.yml
└── traefik/
    └── docker-compose.yml
</code>

Con esto, cada contenedor se llama algo como bookstack-app-1 o traefik-reverse-proxy-1, lo cual te ayuda a localizar problemas rápido: si el contenedor bookstack-app-1 peta, ya sabes exactamente en qué carpeta mirar.

Visualmente es mucho más limpio y permite gestionar cada servicio de forma independiente (arrancarlo, pararlo o actualizarlo sin tocar el resto). Además, hay aplicaciones como Dozzle que se aprovechan de tener stacks separados para organizar mejor los logs.

La contrapartida es que si separas todo demasiado, la coordinación entre servicios comunes (como Traefik o redes compartidas) exige algo más de mimo: hay que declarar redes externas, etiquetas específicas de Traefik y recordar la nomenclatura de las redes creadas por otros docker-compose.

Buenas prácticas con .env, overrides y control de versiones

Uno de los trucos más infravalorados es centralizar la configuración en ficheros .env. En lugar de inundar tu docker-compose.yml con variables de entorno, se define algo así:

<code>DB_USERNAME=myuser
DB_PASSWORD=secretpassword
</code>

Y luego en el YAML se referencian como ${DB_USERNAME} o ${DB_PASSWORD}. Esto hace que el compose se lea de un vistazo, te permite compartir variables entre múltiples servicios y, sobre todo, guardar contraseñas en un fichero separado (que puedes excluir de Git).

Para entornos distintos (producción, pruebas, desarrollo) resulta muy útil aprovechar docker-compose.override.yml. La idea es tener un docker-compose.yml base y, en el override, solo sobrescribir lo que cambia: puertos, rutas, flags de depuración…

Por ejemplo, en desarrollo puedes cargar un override donde expones un puerto distinto, activas DEBUG y montas el código fuente local. No tocas el YAML principal, pero adaptas el stack al entorno donde lo lanzas.

Obviamente, versionar todo con Git es obligatorio si quieres que el homelab sea mínimamente serio. Sueles tener algo como:

<code>homelab-docker/
├── docker-compose.yml
├── .env.example
├── services/
│   ├── media/
│   ├── infra/
│   └── ...
└── scripts/
</code>

A partir de ahí, inicializas el repo, haces commits con los cambios de infraestructura y, si algo se rompe, puedes volver a una versión anterior de tus Compose en segundos. Para homelabs un poco ambiciosos, esto no es una opción, es la única forma de no volverte loco.

Redes, Traefik y exposición segura de servicios

En casi todos los homelabs medianamente avanzados aparece el mismo combo: Traefik como reverse proxy y un proveedor de identidad centralizado (Auth o Authentik). Esto permite exponer muchas apps bajo subdominios con HTTPS y SSO.

Un patrón clásico es montar una red Docker dedicada, por ejemplo reverse_proxy o similar, donde se conectan Traefik y todos los servicios web que vayas a servir hacia fuera. El resto de contenedores (bases de datos, cachés, etc.) permanecen en redes internas aisladas.

Si usas Traefik y separas tus servicios en docker-compose diferentes, necesitas definir una red externa compartida. Algo así:

<code>services:
  bookstack:
    image: lscr.io/linuxserver/bookstack
    networks:
      - traefik-net
    labels:
      - "traefik.docker.network=traefik_default"

networks:
  traefik-net:
    name: traefik_default
    external: true
</code>

Aquí la red traefik_default la crea el stack de Traefik, y el resto de servicios se añaden a ella vía una red externa denominada traefik-net. Las etiquetas le indican a Traefik qué red debe usar para enrutar el tráfico.

Cuando un mismo stack incluye servicios de backend (por ejemplo, un contenedor web y su base de datos) puedes conectarlos a una red por defecto compartida, y solo al contenedor web darle acceso a la red de Traefik. La base de datos tendrá un label traefik.enable=false para que Traefik la ignore.

Este tipo de montaje te da dos beneficios clave: aislamiento entre servicios y exposición controlada. Sólo los contenedores que tú marcas con labels de Traefik y que están en la red del proxy llegan a ser accesibles desde fuera.

Persistencia de datos, volúmenes y estructura de discos

Un homelab sin datos persistentes no sirve de mucho: bases de datos, configuraciones, media, documentos… todo tiene que sobrevivir a un docker compose down. Los volúmenes y los bind mounts son tu seguro de vida.

  Qué es la virtualización y cómo activarla paso a paso

Mucha gente organiza el almacenamiento con una estructura tipo:

<code>/mnt/storage/
├── downloads/
│   ├── movies/
│   └── tv/
├── media/
│   ├── movies/
│   ├── tv/
│   └── music/
└── srv/
    └── <configuraciones de apps>
</code>

La idea es que los descargadores (qBittorrent, SABnzbd, etc.) sólo vean la carpeta downloads, los gestores tipo Radarr/Sonarr tengan acceso tanto a downloads como a media (para mover/crear hardlinks) y los servidores como Plex o Jellyfin solo vean la carpeta media.

De esta forma aplicas el principio de mínimo privilegio: cada contenedor sólo accede a lo que realmente necesita. Y la separación clara ayuda también a la hora de decidir qué volúmenes o rutas se van a respaldar a la nube o a discos externos.

En el directorio srv suelen vivir las configuraciones de las apps (por ejemplo, /srv/jellyfin/config, /srv/traefik, /srv/paperless, etc.). Esto se suele versionar parcialmente (plantillas, Caddyfile, etc.), dejando fuera lo puramente sensible o pesado.

Para algunos casos, es interesante usar hardlinks en la cadena de descarga: servicios tipo Radarr o Sonarr pueden enlazar los ficheros descargados para mantener el seeding sin duplicar espacio en disco. El diseño de directorios propuesto por guías como TRaSHGuides se basa precisamente en esto.

Automatización de despliegues con GitHub Actions y runners locales

Si te gusta rizar el rizo, puedes ir un paso más allá y automatizar la actualización del homelab con CI/CD. Varios usuarios han reemplazado Jenkins y similares por un flujo con GitHub Actions y un runner auto-hospedado en el propio homelab.

El mecanismo es sencillo: cada vez que haces push a la rama principal de tu repo de homelab, se lanza un workflow de GitHub Actions que ejecuta pruebas, linters y, si todo va bien, despliega los cambios en el servidor.

Un flujo típico incluye pasos como:

  • Escáner de secretos tipo Gitleaks: por si por descuido has subido contraseñas o tokens al repo.
  • Linting de YAML o del código de infraestructura, para mantener el formato legible y consistente.
  • Actualización del repositorio en el propio homelab: git pull en el servidor objetivo.
  • Recreación controlada de contenedores: parar los viejos, lanzar los nuevos y verificar estado.

Ventajas: seguridad adicional (controlas fugas de secretos), mejor calidad de código y despliegues repetibles con un solo push. Y como usas un runner local, las imágenes y volúmenes no salen de tu red; simplemente aprovechas la interfaz de GitHub para visualizar los pipelines.

Por qué Docker Compose facilita tanto la vida en un homelab

Mucha gente ha pasado años tirando de docker run y Portainer hasta que, a raíz de un incidente o una migración, se ha visto forzada a re-evaluar su enfoque. Cuando pierdes un host o tienes que mover servicios a otra máquina, depender de comandos sueltos o de configuraciones solo en Portainer es una trampa.

La gran diferencia al pasarte a Compose es que toda la definición del servicio pasa a ser texto: volúmenes, puertos, redes, labels, variables… Todo en un YAML que puedes copiar, compartir, versionar y reutilizar.

Editar un servicio deja de ser “reconstruir un contenedor a mano” y pasa a ser modificar una línea en un fichero, guardar y hacer docker compose up -d. No tienes que acordarte del comando original, ni hacer click por varias pantallas de Portainer.

Además, si trabajas con varios servidores (mini PCs, NAS, sobremesas) es extremadamente cómodo poder copiar un mismo fichero compose a otra máquina, ajustar cuatro rutas y lanzar el mismo stack con otro hardware. De hecho, muchas personas reconocen que, tras un susto de pérdida de datos o migraciones caóticas, Compose les ha salvado mucho tiempo en eventos posteriores.

Como extra, componer nuevos servicios a partir de antiguos se vuelve trivial: por ejemplo, clonar la configuración de Plex para montar Jellyfin reutilizando las mismas rutas de medios y dispositivos de transcodificación apenas lleva unos minutos si lo haces copiando bloques de YAML.

Optimización: build context, multi-stage builds y recursos

Aunque muchos contenedores de homelab vienen de imágenes públicas, en algunos casos compilarás tus propias imágenes. Ahí conviene cuidar el build context: no enviar todo el repositorio sin filtrar, sino limitarte a la carpeta del proyecto (con un buen .dockerignore) para que las builds sean rápidas y ligeras.

Otra técnica muy útil es recurrir a multi-stage builds en tus Dockerfiles: en una primera fase instalas dependencias y compilas, y en una segunda fase copias solo los artefactos necesarios a una imagen base pequeña. Resultado: imágenes finales mucho más pequeñas y seguras, porque no arrastran toolchains ni librerías de más.

  Gestión de procesos en sistemas operativos

En el lado de Compose, tienes la opción de definir límites de CPU y RAM (sobre todo en entornos Swarm o cuando Docker respeta esos parámetros) para evitar que una app glotona acapare recursos. En homelabs esto ayuda a que un servicio mal configurado no deje seco el resto del sistema.

No te olvides de las políticas de reinicio (restart: always, unless-stopped, on-failure): con ellas te aseguras de que los servicios críticos (proxy inverso, VPN, bases de datos clave) vuelvan a levantarse solos después de un reboot o un fallo puntual.

Finalmente, conviene marcar tareas periódicas de limpieza con comandos como docker image prune, docker container prune y docker volume prune para ir eliminando residuos de builds antiguas, contenedores detenidos o volúmenes huérfanos y así recuperar espacio en disco.

Salud de servicios, logging y monitorización

Para que tu homelab no sea una caja negra, es importante trabajar tres patas: healthchecks, logging controlado y monitorización. Docker Compose permite declarar healthcheck por servicio (comandos tipo curl -f http://localhost o scripts específicos) que determinan si un contenedor se considera sano.

Con ello puedes hacer que solo los contenedores “healthy” reciban tráfico (por ejemplo, a través de Traefik) y que, si dejan de responder, se reinicien siguiendo la política configurada. Esto sube bastante la resiliencia con un esfuerzo mínimo.

En cuanto a logs, ajustar el driver json-file con límites de max-size y max-file evita que el disco se llene con gigas de logs olvidados. Herramientas web como Dozzle ayudan a navegar por los logs de todos los contenedores desde un navegador, algo muy cómodo para depurar servicios concretos.

Por la parte de métricas y monitorización continua, el combo clásico es cAdvisor + Prometheus + Grafana. cAdvisor expone estadísticas de uso de CPU, memoria, disco y red por contenedor; Prometheus las recolecta periódicamente y Grafana las muestra en paneles atractivos, con alertas si algo se dispara.

Un homelab bien montado suele incluir además Uptime Kuma para chequeos de disponibilidad (HTTP, ICMP, TCP…) y algún sistema de backups automatizados tipo Duplicati para copiar los datos críticos a otros discos o a la nube. Así sabes qué está pasando y, si algo revienta, no pierdes lo importante.

Seguridad y acceso remoto al homelab

Por muy casero que sea el montaje, la seguridad no es opcional. Mucha gente opta por no exponer directamente su NAS ni sus servicios al exterior, limitando el acceso remoto a través de una VPN (WireGuard es una opción muy popular por rendimiento y sencillez).

En este modelo, el router hace de puerta de entrada: solo se abre un puerto aleatorio hacia el servidor VPN, y una vez conectado, todas las peticiones a servicios internos pasan por un túnel cifrado. Ni Traefik ni las apps se exponen a Internet sin ese filtro previo.

Quien prefiere no gestionar su propia VPN a veces recurre a Cloudflare Tunnel o Tailscale para llegar al homelab sin abrir puertos. Son alternativas cómodas, aunque si tu prioridad absoluta es la privacidad, tendrás que valorar qué metadatos pueden recopilar estos terceros.

Otra buena práctica es cifrar los discos del servidor y del NAS, aplicar parches regularmente y limitar las versiones automáticas (muchos evitan Watchtower en favor de actualizaciones manuales controladas). Mejor ir un poco por detrás pero con control, que romper medio homelab por un update que no has revisado.

Como se ve, no hace falta llegar a un nivel “enterprise”, pero sí conviene establecer unas bases mínimas de seguridad y disciplina para que tu homelab no sea un coladero ni una fuente constante de sustos.

Al final, montar un homelab serio con Docker Compose es una mezcla de organización, sentido común y ganas de cacharrear: si agrupar servicios, defines bien las redes, documentas en Git y automatizas un poco, te queda un entorno que puedes levantar con un solo comando, migrar a otra máquina con facilidad y ampliar poco a poco sin que se convierta en una selva incontrolable.