Introducción: Tu homelab completo en minutos
¿Cansado de instalar servicios uno por uno? Docker Compose convierte tu servidor en un homelab completamente funcional con un solo comando.
En esta guía encontrarás 12 stacks listos para copiar y pegar, desde media centers hasta monitorización, pasando por automatización y productividad. Cada stack incluye configuración completa, explicaciones y mejores prácticas.
- 12 docker-compose.yml funcionales y listos para producción
- Estructura profesional de directorios para homelab
- Gestión de secretos, redes y volúmenes
- Monitorización y logs centralizados
- Troubleshooting de problemas comunes
📋 Contenido de esta guía
- ¿Por qué Docker Compose?
- Estructura base profesional
- Stack 1: Media Center (Jellyfin + Arr)
- Stack 2: n8n (Automatización)
- Stack 3: Nextcloud
- Stack 4: Prometheus + Grafana
- Stack 5: Vaultwarden
- Stack 6: Pi-hole + Unbound
- Stack 7: Home Assistant
- Stack 8: Portainer
- Stack 9: Uptime Kuma
- Stack 10: Nginx Proxy Manager
- Stack 11: Audiobookshelf
- Stack 12: Stirling-PDF
- Gestión avanzada de stacks
- Backups automatizados
- Monitorización de recursos
- Troubleshooting común
- Mejores prácticas
¿Por qué Docker Compose para tu homelab?
Ventajas frente a contenedores individuales
| Aspecto | docker run (individual) | docker-compose.yml |
|---|---|---|
| Complejidad | Comandos largos y difíciles de recordar | ✅ Todo en archivo YAML legible |
| Reproducibilidad | Hay que documentar comandos manualmente | ✅ Archivo = documentación viva |
| Servicios múltiples | Gestionar cada contenedor por separado | ✅ docker compose up -d para todo |
| Dependencias | Orden manual de inicio | ✅ depends_on automático |
| Redes | Configurar manualmente con –network | ✅ Red aislada automática por stack |
| Actualizaciones | Stop, rm, pull, run (4 pasos) | ✅ docker compose pull && up -d |
| Backup config | Scripts personalizados | ✅ Versionar .yml en git |
Casos de uso ideales
- Media Center completo: Plex/Jellyfin + Sonarr + Radarr + qBittorrent
- Stack automatización: n8n + PostgreSQL + Redis
- Monitorización: Prometheus + Grafana + Node Exporter
- Productividad: Nextcloud + MariaDB + Redis + Collabora
- Desarrollo: GitLab + PostgreSQL + Redis + Runner
Estructura base para homelab profesional
Antes de crear stacks, organiza tu servidor con esta estructura recomendada:
/home/usuario/docker/
├── stacks/
│ ├── media/ # Plex, Jellyfin, Arr stack
│ │ └── docker-compose.yml
│ ├── automation/ # n8n, Home Assistant
│ │ └── docker-compose.yml
│ ├── monitoring/ # Prometheus, Grafana
│ │ └── docker-compose.yml
│ ├── productivity/ # Nextcloud, Vaultwarden
│ │ └── docker-compose.yml
│ └── network/ # Pi-hole, Nginx Proxy Manager
│ └── docker-compose.yml
├── data/ # Volúmenes persistentes
│ ├── plex/
│ ├── nextcloud/
│ ├── grafana/
│ └── ...
├── media/ # Contenido multimedia
│ ├── peliculas/
│ ├── series/
│ └── musica/
└── backups/ # Backups automáticos
└── ...
Comando para crear estructura completa:
mkdir -p ~/docker/{stacks/{media,automation,monitoring,productivity,network},data,media/{peliculas,series,musica},backups}
Stack 1: Media Center completo (Jellyfin + Arr)
El stack más popular para homelab: servidor multimedia con descarga y organización automática.
Servicios incluidos:
- Jellyfin: Media server open source (alternativa a Plex)
- Sonarr: Gestión automática de series
- Radarr: Gestión automática de películas
- qBittorrent: Cliente torrent con WebUI
- Prowlarr: Indexer manager (un solo lugar para trackers)
Archivo: ~/docker/stacks/media/docker-compose.yml
version: '3.8'
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Madrid
volumes:
- ~/docker/data/jellyfin/config:/config
- ~/docker/data/jellyfin/cache:/cache
- ~/docker/media:/media
ports:
- "8096:8096"
restart: unless-stopped
networks:
- media
sonarr:
image: linuxserver/sonarr:latest
container_name: sonarr
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Madrid
volumes:
- ~/docker/data/sonarr:/config
- ~/docker/media/series:/tv
- ~/docker/data/qbittorrent/downloads:/downloads
ports:
- "8989:8989"
restart: unless-stopped
networks:
- media
radarr:
image: linuxserver/radarr:latest
container_name: radarr
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Madrid
volumes:
- ~/docker/data/radarr:/config
- ~/docker/media/peliculas:/movies
- ~/docker/data/qbittorrent/downloads:/downloads
ports:
- "7878:7878"
restart: unless-stopped
networks:
- media
prowlarr:
image: linuxserver/prowlarr:latest
container_name: prowlarr
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Madrid
volumes:
- ~/docker/data/prowlarr:/config
ports:
- "9696:9696"
restart: unless-stopped
networks:
- media
qbittorrent:
image: linuxserver/qbittorrent:latest
container_name: qbittorrent
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Madrid
- WEBUI_PORT=8080
volumes:
- ~/docker/data/qbittorrent/config:/config
- ~/docker/data/qbittorrent/downloads:/downloads
ports:
- "8080:8080"
- "6881:6881"
- "6881:6881/udp"
restart: unless-stopped
networks:
- media
networks:
media:
driver: bridge
Desplegar el stack:
cd ~/docker/stacks/media
docker compose up -d
Acceso a servicios:
- Jellyfin:
http://IP:8096 - Sonarr:
http://IP:8989 - Radarr:
http://IP:7878 - Prowlarr:
http://IP:9696 - qBittorrent:
http://IP:8080(user:admin, password en logs:docker logs qbittorrent)
- Configurar Prowlarr → añadir indexers
- En Prowlarr → Apps → añadir Sonarr y Radarr (API keys)
- En Sonarr/Radarr → Download Clients → añadir qBittorrent
- Añadir contenido en Sonarr/Radarr → descarga automática
- Jellyfin detecta nuevos archivos automáticamente
Stack 2: n8n (Automatización avanzada)
Workflow automation potente con persistencia PostgreSQL y caché Redis. Alternativa self-hosted a Zapier/Make.
Archivo: ~/docker/stacks/automation/docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: n8n-postgres
environment:
POSTGRES_DB: n8n
POSTGRES_USER: n8n
POSTGRES_PASSWORD: n8n_secure_password_change_me
volumes:
- ~/docker/data/n8n/postgres:/var/lib/postgresql/data
restart: unless-stopped
networks:
- n8n
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: n8n-redis
volumes:
- ~/docker/data/n8n/redis:/data
restart: unless-stopped
networks:
- n8n
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
n8n:
image: n8nio/n8n:latest
container_name: n8n
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=n8n_secure_password_change_me
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_BULL_REDIS_PORT=6379
- N8N_ENCRYPTION_KEY=change_this_to_random_32_chars
- WEBHOOK_URL=https://n8n.tudominio.com/
- GENERIC_TIMEZONE=Europe/Madrid
- N8N_METRICS=true
- N8N_LOG_LEVEL=info
volumes:
- ~/docker/data/n8n/data:/home/node/.n8n
ports:
- "5678:5678"
restart: unless-stopped
networks:
- n8n
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
n8n:
driver: bridge
Acceso: http://IP:5678
- Cambiar
POSTGRES_PASSWORDpor contraseña fuerte - Cambiar
N8N_ENCRYPTION_KEYa string aleatorio de 32 caracteres - Si expones a internet, usar Cloudflare Tunnel o Nginx + autenticación
Generar encryption key segura:
openssl rand -base64 32
Stack 3: Nextcloud (Nube privada completa)
Tu propia nube con oficina online, calendario, contactos y sincronización de archivos.
Archivo: ~/docker/stacks/productivity/docker-compose.yml
version: '3.8'
services:
mariadb:
image: mariadb:11
container_name: nextcloud-db
command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
environment:
MYSQL_ROOT_PASSWORD: rootpassword_change_me
MYSQL_DATABASE: nextcloud
MYSQL_USER: nextcloud
MYSQL_PASSWORD: nextcloud_password_change_me
volumes:
- ~/docker/data/nextcloud/mariadb:/var/lib/mysql
restart: unless-stopped
networks:
- nextcloud
redis:
image: redis:7-alpine
container_name: nextcloud-redis
restart: unless-stopped
networks:
- nextcloud
nextcloud:
image: nextcloud:latest
container_name: nextcloud
environment:
- MYSQL_HOST=mariadb
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=nextcloud_password_change_me
- REDIS_HOST=redis
- NEXTCLOUD_TRUSTED_DOMAINS=nextcloud.tudominio.com IP_DE_TU_SERVIDOR
- OVERWRITEPROTOCOL=https
- OVERWRITEHOST=nextcloud.tudominio.com
volumes:
- ~/docker/data/nextcloud/html:/var/www/html
- ~/docker/data/nextcloud/data:/var/www/html/data
ports:
- "8081:80"
restart: unless-stopped
networks:
- nextcloud
depends_on:
- mariadb
- redis
# Opcional: Collabora Online (LibreOffice online)
collabora:
image: collabora/code:latest
container_name: collabora
environment:
- domain=nextcloud\\.tudominio\\.com
- username=admin
- password=collabora_admin_password
- extra_params=--o:ssl.enable=false --o:ssl.termination=true
ports:
- "9980:9980"
restart: unless-stopped
networks:
- nextcloud
cap_add:
- MKNOD
networks:
nextcloud:
driver: bridge
Acceso: http://IP:8081
Primer inicio (crear usuario admin):
- Ir a
http://IP:8081 - Crear usuario administrador
- Sistema detecta automáticamente MariaDB y Redis
- Instalar apps recomendadas: Calendar, Contacts, Tasks, Notes
Stack 4: Monitorización completa (Prometheus + Grafana)
Monitoriza tu homelab con métricas detalladas y dashboards profesionales.
Archivo: ~/docker/stacks/monitoring/docker-compose.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d'
volumes:
- ~/docker/data/prometheus/config:/etc/prometheus
- ~/docker/data/prometheus/data:/prometheus
ports:
- "9090:9090"
restart: unless-stopped
networks:
- monitoring
grafana:
image: grafana/grafana:latest
container_name: grafana
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin_change_me
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource
volumes:
- ~/docker/data/grafana:/var/lib/grafana
ports:
- "3000:3000"
restart: unless-stopped
networks:
- monitoring
depends_on:
- prometheus
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
command:
- '--path.rootfs=/host'
volumes:
- '/:/host:ro,rslave'
ports:
- "9100:9100"
restart: unless-stopped
networks:
- monitoring
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
ports:
- "8082:8080"
restart: unless-stopped
networks:
- monitoring
privileged: true
devices:
- /dev/kmsg
networks:
monitoring:
driver: bridge
Archivo de configuración Prometheus:
Crear ~/docker/data/prometheus/config/prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
Acceso:
- Prometheus:
http://IP:9090 - Grafana:
http://IP:3000(admin/admin_change_me) - Node Exporter:
http://IP:9100 - cAdvisor:
http://IP:8082
Configurar Grafana (primer uso):
- Login en Grafana
- Configuration → Data Sources → Add Prometheus
- URL:
http://prometheus:9090→ Save & Test - Dashboards → Import → ID 1860 (Node Exporter Full) → Load
- Dashboards → Import → ID 193 (Docker containers) → Load
Stack 5: Vaultwarden (Gestor de contraseñas)
Implementación ligera de Bitwarden. Gestor de contraseñas self-hosted compatible con apps oficiales de Bitwarden.
Archivo: ~/docker/stacks/productivity/vaultwarden-compose.yml
version: '3.8'
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
environment:
- DOMAIN=https://vault.tudominio.com
- SIGNUPS_ALLOWED=true # Cambiar a false después de crear cuenta
- ADMIN_TOKEN=admin_token_change_to_random_string
- SMTP_HOST=smtp.gmail.com
- SMTP_FROM=tuemail@gmail.com
- SMTP_PORT=587
- SMTP_SECURITY=starttls
- SMTP_USERNAME=tuemail@gmail.com
- SMTP_PASSWORD=tu_app_password
volumes:
- ~/docker/data/vaultwarden:/data
ports:
- "8083:80"
restart: unless-stopped
networks:
default:
driver: bridge
Acceso: http://IP:8083
Panel admin: http://IP:8083/admin (usar ADMIN_TOKEN)
- SIEMPRE exponer con HTTPS (Cloudflare Tunnel o reverse proxy con SSL)
- Cambiar
SIGNUPS_ALLOWED=falsedespués de crear tu cuenta - Generar ADMIN_TOKEN fuerte:
openssl rand -base64 48 - Configurar SMTP para recuperación de contraseña
- Hacer backups regulares de
~/docker/data/vaultwarden
Stack 6: Pi-hole + Unbound (DNS privado y bloqueador de ads)
Bloquea publicidad y trackers a nivel de red para todos tus dispositivos.
Archivo: ~/docker/stacks/network/docker-compose.yml
version: '3.8'
services:
pihole:
image: pihole/pihole:latest
container_name: pihole
environment:
TZ: 'Europe/Madrid'
WEBPASSWORD: 'admin_password_change_me'
PIHOLE_DNS_: '127.0.0.1#5335' # Usar Unbound
DNSMASQ_LISTENING: 'all'
volumes:
- ~/docker/data/pihole/etc-pihole:/etc/pihole
- ~/docker/data/pihole/etc-dnsmasq.d:/etc/dnsmasq.d
ports:
- "53:53/tcp"
- "53:53/udp"
- "8084:80/tcp"
restart: unless-stopped
networks:
- dns
depends_on:
- unbound
unbound:
image: mvance/unbound:latest
container_name: unbound
volumes:
- ~/docker/data/unbound:/opt/unbound/etc/unbound
ports:
- "5335:53/tcp"
- "5335:53/udp"
restart: unless-stopped
networks:
dns:
ipv4_address: 172.20.0.2
networks:
dns:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24
Acceso: http://IP:8084/admin
Configuración post-instalación:
- Login con contraseña de WEBPASSWORD
- Settings → DNS → Upstream DNS servers → Verificar que apunta a Unbound (127.0.0.1#5335)
- Adlists → Añadir listas recomendadas (StevenBlack, OISD, etc.)
- Tools → Update Gravity
- En tu router: Cambiar DNS a IP del servidor Pi-hole
Stack 7: Home Assistant (Domótica completa)
Control total de tu smart home con integraciones ilimitadas.
Archivo: ~/docker/stacks/automation/homeassistant-compose.yml
version: '3.8'
services:
homeassistant:
image: ghcr.io/home-assistant/home-assistant:stable
container_name: homeassistant
environment:
- TZ=Europe/Madrid
volumes:
- ~/docker/data/homeassistant/config:/config
- /etc/localtime:/etc/localtime:ro
network_mode: host
restart: unless-stopped
privileged: true
# Opcional: MQTT Broker (para dispositivos Zigbee/WiFi)
mosquitto:
image: eclipse-mosquitto:latest
container_name: mosquitto
volumes:
- ~/docker/data/mosquitto/config:/mosquitto/config
- ~/docker/data/mosquitto/data:/mosquitto/data
- ~/docker/data/mosquitto/log:/mosquitto/log
ports:
- "1883:1883"
- "9001:9001"
restart: unless-stopped
# Opcional: Zigbee2MQTT (para dispositivos Zigbee)
zigbee2mqtt:
image: koenkk/zigbee2mqtt:latest
container_name: zigbee2mqtt
environment:
- TZ=Europe/Madrid
volumes:
- ~/docker/data/zigbee2mqtt:/app/data
- /run/udev:/run/udev:ro
devices:
- /dev/ttyUSB0:/dev/ttyUSB0 # Adaptador Zigbee
ports:
- "8085:8080"
restart: unless-stopped
privileged: true
Acceso: http://IP:8123
Configuración mosquitto:
Crear ~/docker/data/mosquitto/config/mosquitto.conf:
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
listener 1883
allow_anonymous true
Home Assistant tiene nodo oficial en n8n. Puedes crear automatizaciones que combinen:
- Eventos de sensores → notificaciones Telegram
- Webhooks externos → activar escenas en casa
- Calendario → encender luces automáticamente
Stack 8: Portainer (Gestión visual de Docker)
Interfaz web para gestionar todos tus contenedores, stacks y volúmenes.
Archivo: ~/docker/stacks/management/docker-compose.yml
version: '3.8'
services:
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
command: -H unix:///var/run/docker.sock
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ~/docker/data/portainer:/data
ports:
- "9443:9443"
- "9000:9000"
restart: unless-stopped
Acceso: https://IP:9443
Funcionalidades destacadas:
- Ver todos los contenedores, imágenes, volúmenes y redes
- Iniciar/parar/reiniciar contenedores con un click
- Ver logs en tiempo real
- Acceder a terminal de contenedores
- Gestionar stacks completos (equivalente a docker-compose)
- Monitorización de recursos (CPU, RAM, red)
- Actualizar imágenes con un click
Stack 9: Uptime Kuma (Monitorización de servicios)
Monitoriza disponibilidad de tus servicios y recibe alertas cuando caigan.
Archivo: ~/docker/stacks/monitoring/uptime-kuma-compose.yml
version: '3.8'
services:
uptime-kuma:
image: louislam/uptime-kuma:latest
container_name: uptime-kuma
volumes:
- ~/docker/data/uptime-kuma:/app/data
ports:
- "3001:3001"
restart: unless-stopped
Acceso: http://IP:3001
Tipos de monitorización:
- HTTP(s): Verificar que web responde (status 200)
- TCP Port: Verificar puerto abierto (SSH, bases de datos)
- Ping: Verificar host alcanzable
- DNS: Resolver nombres de dominio
- Docker: Estado de contenedores específicos
Notificaciones soportadas: Telegram, Discord, Email, Slack, Webhook, Gotify, Pushover, Signal, y 90+ más.
Stack 10: Nginx Proxy Manager (Reverse Proxy visual)
Gestiona reverse proxies, SSL automático y acceso a servicios con dominios limpios.
Archivo: ~/docker/stacks/network/npm-compose.yml
version: '3.8'
services:
nginx-proxy-manager:
image: jc21/nginx-proxy-manager:latest
container_name: nginx-proxy-manager
environment:
DB_SQLITE_FILE: "/data/database.sqlite"
volumes:
- ~/docker/data/nginx-proxy-manager/data:/data
- ~/docker/data/nginx-proxy-manager/letsencrypt:/etc/letsencrypt
ports:
- "80:80"
- "443:443"
- "81:81"
restart: unless-stopped
Acceso admin: http://IP:81
Credenciales por defecto:
- Email:
admin@example.com - Password:
changeme - (Cambiar inmediatamente al primer login)
Ejemplo de configuración:
Para acceder a Jellyfin mediante https://jellyfin.tudominio.com:
- Hosts → Proxy Hosts → Add Proxy Host
- Domain Names:
jellyfin.tudominio.com - Scheme:
http - Forward Hostname/IP:
jellyfin(nombre del contenedor) - Forward Port:
8096 - SSL → Request SSL Certificate → Force SSL
- Save
- Tener dominio propio
- Apuntar DNS A record a tu IP pública
- Puertos 80 y 443 abiertos en router (port forward)
- NPM gestionará certificados Let’s Encrypt automáticamente
Stack 11: Audiobookshelf (Biblioteca de audiolibros y podcasts)
Gestiona y escucha tu colección de audiolibros y podcasts con apps móviles nativas.
Archivo: ~/docker/stacks/media/audiobookshelf-compose.yml
version: '3.8'
services:
audiobookshelf:
image: ghcr.io/advplyr/audiobookshelf:latest
container_name: audiobookshelf
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Madrid
volumes:
- ~/docker/data/audiobookshelf/config:/config
- ~/docker/data/audiobookshelf/metadata:/metadata
- ~/docker/media/audiobooks:/audiobooks
- ~/docker/media/podcasts:/podcasts
ports:
- "13378:80"
restart: unless-stopped
Acceso: http://IP:13378
Características destacadas:
- Reproductor web con velocidad variable
- Apps nativas iOS/Android
- Seguimiento automático de progreso
- Metadata automática (covers, descripciones, autores)
- Gestión de podcasts con suscripciones
- Multi-usuario con progreso independiente
Stack 12: Stirling-PDF (Herramientas PDF todo en uno)
Suite completa de herramientas PDF self-hosted: merge, split, OCR, comprimir, convertir…
Archivo: ~/docker/stacks/productivity/stirling-pdf-compose.yml
version: '3.8'
services:
stirling-pdf:
image: frooodle/s-pdf:latest
container_name: stirling-pdf
environment:
- DOCKER_ENABLE_SECURITY=false # true para login obligatorio
- INSTALL_BOOK_AND_ADVANCED_HTML_OPS=true
- LANGS=es_ES
volumes:
- ~/docker/data/stirling-pdf/trainingData:/usr/share/tesseract-ocr/5/tessdata
- ~/docker/data/stirling-pdf/extraConfigs:/configs
- ~/docker/data/stirling-pdf/logs:/logs
ports:
- "8086:8080"
restart: unless-stopped
Acceso: http://IP:8086
Operaciones disponibles (40+):
| Categoría | Funciones |
|---|---|
| General | Merge, Split, Rotate, Reorder, Extract pages |
| Conversión | PDF ↔ Images, Word, Excel, PowerPoint, HTML |
| Seguridad | Add/Remove password, Permisos, Firmas digitales |
| OCR | Extraer texto de PDFs escaneados (30+ idiomas) |
| Compresión | Reducir tamaño, Optimizar para web |
| Edición | Add watermark, Header/Footer, Page numbers |
Alternativa 100% local a: Adobe Acrobat, SmallPDF, iLovePDF, PDF24.
Gestión avanzada de stacks
Comandos esenciales
# Levantar stack
cd ~/docker/stacks/media
docker compose up -d
# Ver logs de stack completo
docker compose logs -f
# Ver logs de servicio específico
docker compose logs -f jellyfin
# Parar stack sin eliminar contenedores
docker compose stop
# Parar y eliminar contenedores (mantiene volúmenes)
docker compose down
# Eliminar todo incluyendo volúmenes (PELIGRO: pierdes datos)
docker compose down -v
# Actualizar imágenes y recrear contenedores
docker compose pull
docker compose up -d
# Ver estado de servicios
docker compose ps
# Reiniciar servicio específico
docker compose restart jellyfin
# Ejecutar comando en contenedor
docker compose exec jellyfin bash
Variables de entorno con archivos .env
Para no hardcodear contraseñas, usa archivo .env junto al docker-compose.yml:
Ejemplo: ~/docker/stacks/automation/.env
POSTGRES_PASSWORD=mi_password_super_segura
N8N_ENCRYPTION_KEY=gQKxvVHm8fJ2nL5pR9sT3wX7yZ1aC4eF
WEBHOOK_URL=https://n8n.midominio.com/
Uso en docker-compose.yml:
services:
postgres:
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
n8n:
environment:
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
WEBHOOK_URL: ${WEBHOOK_URL}
- Añadir
.enva.gitignore(nunca commitear secretos) - Crear
.env.examplecon variables vacías como plantilla - Permisos restrictivos:
chmod 600 .env
Gestión de redes entre stacks
Para que servicios de diferentes stacks se comuniquen, crea red externa:
# Crear red compartida
docker network create homelab
# En cada docker-compose.yml que necesite comunicarse:
networks:
default:
name: homelab
external: true
Ejemplo: n8n comunicándose con Jellyfin
Ambos stacks usan network: homelab → n8n puede hacer requests a http://jellyfin:8096
Backups automatizados
Estrategia recomendada para proteger datos de tu homelab:
Script de backup diario
Archivo: ~/docker/backup.sh
#!/bin/bash
# Backup automático de volúmenes Docker
BACKUP_DIR=~/docker/backups
DATE=$(date +%Y%m%d_%H%M%S)
# Crear directorio de backup del día
mkdir -p "$BACKUP_DIR/$DATE"
# Servicios críticos a respaldar
SERVICES=(
"nextcloud"
"vaultwarden"
"n8n"
"grafana"
"homeassistant"
)
echo "🔄 Iniciando backup: $DATE"
for service in "${SERVICES[@]}"; do
echo "📦 Backup de $service..."
# Parar servicio (opcional, recomendado para consistencia)
cd ~/docker/stacks/*/ 2>/dev/null
docker compose stop "$service" 2>/dev/null
# Comprimir volumen
tar -czf "$BACKUP_DIR/$DATE/${service}.tar.gz" \
-C ~/docker/data "$service"
# Reiniciar servicio
docker compose start "$service" 2>/dev/null
echo "✅ $service respaldado"
done
# Comprimir también docker-compose.yml de cada stack
echo "📝 Backup de configuraciones..."
tar -czf "$BACKUP_DIR/$DATE/stacks-config.tar.gz" \
-C ~/docker stacks
# Eliminar backups antiguos (mantener últimos 7 días)
find "$BACKUP_DIR" -type d -mtime +7 -exec rm -rf {} +
echo "✅ Backup completado: $BACKUP_DIR/$DATE"
echo "📊 Tamaño total: $(du -sh $BACKUP_DIR/$DATE | cut -f1)"
Hacer ejecutable:
chmod +x ~/docker/backup.sh
Programar con cron (diario a las 3am):
crontab -e
# Añadir línea:
0 3 * * * /home/usuario/docker/backup.sh >> /home/usuario/docker/backup.log 2>&1
Backup offsite con rclone
Para copias de seguridad en la nube (Google Drive, Dropbox, B2, S3…):
# Instalar rclone
curl https://rclone.org/install.sh | sudo bash
# Configurar remote (ejemplo: Google Drive)
rclone config
# Subir backups a la nube
rclone sync ~/docker/backups remote:homelab-backups \
--progress \
--checksum \
--log-file ~/docker/rclone.log
Monitorización de recursos
Ver consumo en tiempo real
# Stats de todos los contenedores
docker stats
# Stats de stack específico
cd ~/docker/stacks/media
docker compose ps -q | xargs docker stats
# Ver top 5 contenedores por CPU
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}" | sort -k2 -rh | head -6
# Ver top 5 contenedores por RAM
docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}" | sort -k2 -rh | head -6
Limpiar recursos no utilizados
# Ver espacio usado por Docker
docker system df
# Eliminar contenedores parados
docker container prune -f
# Eliminar imágenes sin usar
docker image prune -a -f
# Eliminar volúmenes huérfanos (CUIDADO)
docker volume prune -f
# Limpiar todo (contenedores parados, imágenes sin usar, redes, cache)
docker system prune -a --volumes -f
# Cleanup semanal automatizado (cron)
0 4 * * 0 docker system prune -a -f >> /var/log/docker-cleanup.log 2>&1
Troubleshooting común
Problema 1: «Cannot connect to the Docker daemon»
Síntoma: docker compose up falla con error de conexión al daemon.
Soluciones:
# Verificar que Docker está corriendo
sudo systemctl status docker
# Iniciar Docker si está parado
sudo systemctl start docker
# Añadir usuario al grupo docker (logout/login después)
sudo usermod -aG docker $USER
# Verificar permisos de socket
sudo chmod 666 /var/run/docker.sock
Problema 2: Contenedor reinicia constantemente
Síntoma: docker compose ps muestra contenedor en estado «Restarting».
Diagnóstico:
# Ver logs del contenedor
docker compose logs jellyfin
# Ver últimas 50 líneas en tiempo real
docker compose logs -f --tail=50 jellyfin
# Causas comunes:
# - Permisos incorrectos en volúmenes (PUID/PGID)
# - Puerto ya en uso
# - Variable de entorno faltante/incorrecta
# - Dependencia no disponible
Solución permisos:
# Verificar UID/GID de tu usuario
id
# Ejemplo output: uid=1000(usuario) gid=1000(usuario)
# Asegúrate que PUID=1000 y PGID=1000 en docker-compose.yml
# Arreglar permisos de volúmenes
sudo chown -R 1000:1000 ~/docker/data/jellyfin
Problema 3: «Port is already allocated»
Síntoma: Error al iniciar stack porque puerto ya está en uso.
Diagnóstico:
# Ver qué está usando puerto 8096
sudo lsof -i :8096
# O con netstat
sudo netstat -tulpn | grep 8096
# Ver todos los puertos usados por Docker
docker ps --format "table {{.Names}}\t{{.Ports}}"
Soluciones:
- Cambiar puerto externo en docker-compose.yml:
ports: - "8097:8096" - Parar servicio conflictivo:
sudo systemctl stop apache2 - Eliminar contenedor duplicado:
docker rm -f contenedor_duplicado
Problema 4: Contenedor no puede acceder a internet
Síntoma: Servicios no pueden descargar actualizaciones o acceder a APIs externas.
Diagnóstico:
# Test DNS dentro del contenedor
docker compose exec jellyfin ping -c 3 google.com
# Verificar configuración DNS del contenedor
docker compose exec jellyfin cat /etc/resolv.conf
Solución:
Añadir DNS públicos en docker-compose.yml:
services:
jellyfin:
dns:
- 8.8.8.8
- 1.1.1.1
Problema 5: Volumen lleno / Sin espacio en disco
Síntoma: Errores de escritura, contenedores fallan al guardar datos.
Diagnóstico:
# Ver espacio en disco
df -h
# Ver tamaño de directorios Docker
du -sh ~/docker/data/*
# Ver espacio usado por Docker
docker system df -v
Soluciones:
# Limpiar logs antiguos de contenedores
truncate -s 0 /var/lib/docker/containers/*/*-json.log
# Limitar tamaño de logs (añadir a docker-compose.yml)
services:
jellyfin:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# Limpiar imágenes y contenedores no usados
docker system prune -a --volumes -f
Problema 6: Cambios en docker-compose.yml no se aplican
Síntoma: Modificas docker-compose.yml pero contenedor mantiene configuración antigua.
Solución:
# Recrear contenedores (mantiene volúmenes)
docker compose up -d --force-recreate
# Si cambias imagen o build context
docker compose up -d --build
# Borrar todo y empezar de cero
docker compose down
docker compose up -d
Mejores prácticas
Seguridad
- ✅ Nunca usar
latesten producción → Pin a versiones específicas:jellyfin:10.8.13 - ✅ Usar .env para secretos → No hardcodear contraseñas en .yml
- ✅ Cambiar credenciales por defecto → admin/admin es puerta abierta
- ✅ Red bridge aislada por stack → No expongas todo a
network_mode: host - ✅ Reverse proxy con SSL → Nginx Proxy Manager o Cloudflare Tunnel
- ✅ Firewall activo →
ufw allow 80/tcp && ufw enable - ✅ Actualizaciones regulares →
docker compose pull && up -dsemanal - ✅ Backups automáticos → Cron diario + offsite semanal
Performance
- ✅ Limitar recursos: Evita que un contenedor consuma todo
services:
jellyfin:
deploy:
resources:
limits:
cpus: '2'
memory: 4G
reservations:
memory: 2G
- ✅ Healthchecks: Restart automático si servicio falla
services:
jellyfin:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8096/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
- ✅ Logs rotativos: Evita llenar disco con logs
services:
jellyfin:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "5"
Mantenibilidad
- ✅ Nombres descriptivos de servicios →
jellyfinmejor quemedia1 - ✅ Comentarios en .yml → Documenta variables críticas
- ✅ Estructura organizada → Un stack por categoría de servicios
- ✅ Versionar en git → Historial de cambios en configuración
- ✅ README por stack → Documenta propósito y configuración especial
Siguientes pasos
Ahora que tienes homelab completo con Docker Compose, considera:
- Migrar a Kubernetes (k3s) si necesitas orquestación avanzada (50+ contenedores)
- Implementar CI/CD con Gitea + Drone para auto-deploy de stacks
- Monitorización avanzada con Loki + Promtail para logs centralizados
- Alta disponibilidad con Docker Swarm (múltiples nodos)
- Integración n8n para automatizar gestión de contenedores (webhooks de actualizaciones)
- VPN permanente con WireGuard para acceso seguro remoto
- Storage distribuido con GlusterFS/Ceph para redundancia de datos
Recursos adicionales
- Documentación Docker Compose: docs.docker.com/compose/
- Awesome Selfhosted: Lista de 1000+ servicios self-hosted
- LinuxServer.io: Imágenes Docker mantenidas y optimizadas
- r/selfhosted: Comunidad activa con ideas y troubleshooting
- Docker Hub: Busca imágenes oficiales y verificadas
Conclusión
Docker Compose transforma la complejidad de gestionar múltiples servicios en archivos YAML simples y reproducibles. Con los 12 stacks de esta guía tienes:
- 🎬 Media center completo con descarga automática
- 🤖 Automatización potente con n8n
- ☁️ Nube privada con Nextcloud
- 📊 Monitorización profesional con Grafana
- 🔒 Gestor de contraseñas auto-hospedado
- 🏠 Domótica con Home Assistant
- 🛠️ Gestión visual con Portainer
- 🌐 Reverse proxy con SSL automático
Todo con comandos simples, configuración versionable y recuperación rápida ante fallos.
Copia, pega, modifica. Estos stacks son punto de partida probado en producción. Ajusta a tus necesidades, añade servicios nuevos y comparte tus mejoras con la comunidad.
¡Bienvenido al mundo del self-hosting profesional! 🚀
