Si buscas un servidor web moderno con Caddy Docker Compose, has llegado al lugar correcto. Caddy es un servidor HTTP/HTTPS que se destaca por su configuración automática de certificados SSL, su sintaxis simple y su potente funcionalidad como reverse proxy. En esta guía completa aprenderás a desplegar Caddy con Docker Compose paso a paso.
¿Qué es Caddy y Por Qué Usarlo?
Caddy es un servidor web escrito en Go que revoluciona la forma en que configuramos HTTPS. A diferencia de Nginx o Apache, esta implementación con Caddy Docker Compose obtiene y renueva certificados SSL automáticamente mediante Let’s Encrypt, sin necesidad de configuración manual ni scripts adicionales.
Con más de 68,400 estrellas en GitHub, esta herramienta se ha convertido en la opción preferida para quienes buscan simplicidad sin sacrificar potencia. Su documentación oficial es clara y completa, facilitando su implementación en cualquier proyecto.
Ventajas de Caddy Docker Compose
Al combinar Caddy con Docker Compose obtienes una solución poderosa para gestionar tus aplicaciones web:
- HTTPS automático: Caddy gestiona certificados SSL sin intervención manual
- HTTP/3 nativo: Soporte completo para el protocolo más moderno
- Configuración simple: El Caddyfile es mucho más legible que la configuración de Nginx
- Reverse proxy eficiente: Enruta tráfico a múltiples contenedores fácilmente
- Sin dependencias: Binario único sin requerimientos externos
- Recarga dinámica: Actualiza configuración sin reiniciar el servicio
Requisitos Previos
Antes de comenzar con tu instalación de Caddy Docker Compose, asegúrate de tener:
- Docker y Docker Compose instalados en tu sistema
- Un dominio apuntando a la IP de tu servidor (registro A o AAAA)
- Puertos 80 y 443 abiertos en tu firewall
- Conocimientos básicos de YAML y línea de comandos
Instalación de Caddy Docker Compose
Vamos a crear una configuración completa paso a paso. Primero, crea un directorio para tu proyecto:
mkdir caddy-server && cd caddy-server
Ahora crea el archivo docker-compose.yml con la siguiente configuración:
version: '3.8'
services:
caddy:
image: caddy:2-alpine
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
networks:
- web
# Aplicación de ejemplo
whoami:
image: traefik/whoami
container_name: whoami
restart: unless-stopped
networks:
- web
volumes:
caddy_data:
caddy_config:
networks:
web:
driver: bridge
Esta configuración usa la imagen oficial Alpine, que es ligera y optimizada. Los volúmenes persistentes aseguran que tus certificados SSL sobrevivan a reinicios del contenedor.
Configuración del Caddyfile
El Caddyfile es el corazón de tu configuración Caddy Docker Compose. Crea el archivo Caddyfile en el mismo directorio:
# Ejemplo básico de reverse proxy
ejemplo.com {
reverse_proxy whoami:80
encode gzip
log {
output file /data/access.log
}
}
# Múltiples subdominios
app.ejemplo.com {
reverse_proxy app:3000
}
api.ejemplo.com {
reverse_proxy api:8080
header {
Access-Control-Allow-Origin *
}
}
La sintaxis es intuitiva: especificas el dominio seguido de directivas entre llaves. El servidor maneja automáticamente la obtención de certificados SSL para cada dominio configurado.
Reverse Proxy con Caddy Docker Compose
Una de las funcionalidades más potentes de Caddy Docker Compose es usar el servicio como reverse proxy. Esto permite centralizar el tráfico HTTPS y distribuirlo a múltiples aplicaciones backend.
Aquí tienes un ejemplo avanzado para configurar múltiples servicios:
# Nextcloud con headers especiales
cloud.ejemplo.com {
reverse_proxy nextcloud:80
header {
Strict-Transport-Security "max-age=31536000;"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
}
}
# WordPress con caché
blog.ejemplo.com {
reverse_proxy wordpress:80
encode zstd gzip
@static {
path *.jpg *.jpeg *.png *.gif *.webp *.css *.js
}
header @static Cache-Control "max-age=31536000"
}
# Aplicación Node.js con WebSocket
chat.ejemplo.com {
reverse_proxy app:3000
@websocket {
header Connection *Upgrade*
header Upgrade websocket
}
}
Labels de Docker para Configuración Automática
Existe un plugin llamado caddy-docker-proxy que permite configurar el reverse proxy mediante labels de Docker, similar a Traefik:
version: '3.8'
services:
caddy:
image: lucaslorentz/caddy-docker-proxy:latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- caddy_data:/data
networks:
- web
myapp:
image: nginx:alpine
labels:
caddy: myapp.ejemplo.com
caddy.reverse_proxy: "{{upstreams 80}}"
networks:
- web
volumes:
caddy_data:
networks:
web:
driver: bridge
Este enfoque escanea automáticamente los contenedores en busca de labels y configura las rutas correspondientes.
Comparativa: Caddy vs Nginx vs Traefik
Cada reverse proxy tiene sus fortalezas. Aquí te presento una comparación objetiva:
| Característica | Caddy | Nginx | Traefik |
|---|---|---|---|
| SSL Automático | ✅ Integrado | ❌ Manual | ✅ Integrado |
| Configuración | Muy simple | Compleja | Media |
| HTTP/3 | ✅ Nativo | ⚠️ Experimental | ✅ Soportado |
| Rendimiento | Excelente | Excepcional | Muy bueno |
| Curva aprendizaje | Baja | Alta | Media |
| Documentación | Excelente | Extensa | Buena |
Para proyectos pequeños y medianos, Caddy Docker Compose ofrece una simplicidad de configuración imbatible. Si necesitas el máximo rendimiento en producción a gran escala, Nginx sigue siendo líder. Traefik brilla en entornos Kubernetes.
Certificados comodín con el reto DNS-01
El método por defecto valida el dominio sirviendo un fichero por HTTP, lo que obliga a que el servidor sea accesible desde Internet en el puerto 80. Para un servicio interno que nunca se publica, eso no vale. La alternativa es el reto DNS-01: en lugar de responder por web, se crea un registro temporal en tu zona DNS.
Tiene dos ventajas grandes. La primera es que funciona sin exponer absolutamente nada. La segunda es que permite emitir un certificado comodín, válido para todos los subdominios de golpe, en lugar de uno por servicio.
La pega es que la imagen oficial no incluye los módulos de proveedores DNS, así que hay que construir una propia. Es más sencillo de lo que suena:
FROM caddy:2-builder AS builder
RUN xcaddy build --with github.com/caddy-dns/cloudflare
FROM caddy:2
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
Ese xcaddy es el compilador oficial del proyecto y existen módulos para la mayoría de proveedores, no solo el del ejemplo. Después se referencia la imagen construida desde el Compose y se le pasa el token de la API.
services:
caddy:
build: .
restart: unless-stopped
environment:
- CF_API_TOKEN=${CF_API_TOKEN}
ports:
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
Y en la configuración se declara el comodín una sola vez:
*.midominio.es {
tls {
dns cloudflare {env.CF_API_TOKEN}
}
@jellyfin host jellyfin.midominio.es
handle @jellyfin {
reverse_proxy jellyfin:8096
}
@vault host vault.midominio.es
handle @vault {
reverse_proxy vaultwarden:80
}
}
Sobre el token: créalo con permisos mínimos, solo edición de zona DNS y limitado al dominio concreto. Un token con acceso completo a la cuenta dentro de un contenedor es un riesgo innecesario, y estos proveedores permiten acotarlo en dos clics.
Un detalle práctico: la propagación de DNS no es instantánea, así que la primera emisión puede tardar un par de minutos. Si ves errores de validación en los registros, espera antes de tocar nada. Y cuidado con el límite de emisiones de Let’s Encrypt mientras pruebas: usa su entorno de pruebas hasta tener la configuración estable.
Fragmentos reutilizables para no repetirte
Cuando el fichero crece y tienes diez servicios detrás, copiar y pegar los mismos bloques de cabeceras acaba en configuraciones incoherentes. Los fragmentos con nombre resuelven eso: se definen una vez y se importan donde haga falta.
(seguridad) {
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
}
(interno) {
@externo not remote_ip 192.168.1.0/24
respond @externo "No autorizado" 403
}
jellyfin.midominio.es {
import seguridad
reverse_proxy jellyfin:8096
}
panel.midominio.es {
import seguridad
import interno
reverse_proxy portainer:9000
}
Ese fragmento interno es especialmente útil: te permite tener un único servidor sirviendo cosas públicas y cosas restringidas a la red local, sin montar dos instancias. Lo que quede fuera del rango recibe un 403 y nunca llega al servicio de detrás.
Antes de recargar, valida siempre el fichero. Un error de sintaxis con el servicio ya en marcha deja todos tus dominios caídos a la vez, que es justo el riesgo de centralizar el acceso en un único proxy.
docker compose exec caddy caddy validate --config /etc/caddy/Caddyfile
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile
La recarga se aplica sin cortar las conexiones en curso, así que puedes ajustar la configuración en caliente sin que nadie note nada. Es una de esas cosas pequeñas que se agradecen mucho cuando el servidor da servicio a más gente que tú.
Un último apunte sobre convivencia: si ya tienes otro servicio ocupando los puertos 80 y 443 —otro proxy, un servidor web antiguo— no podrás levantar este hasta liberarlos. Es el error más frecuente en la primera instalación y se diagnostica en un segundo con ss -tlnp | grep -E ":80|:443". Conviene decidir un único punto de entrada para toda la máquina en lugar de repartir dominios entre dos proxies distintos, porque la gestión de certificados se vuelve un lío difícil de seguir.
Y si vienes de una configuración previa con otro proxy, migra un dominio primero y comprueba que emite certificado antes de mover el resto. Hacerlo de golpe con diez servicios es la receta para pasarse la tarde depurando a ciegas cuál de todos falla, cuando además el límite de emisiones puede bloquearte los reintentos durante horas. Con actualizaciones automáticas activadas, deja este contenedor fuera: prefieres enterarte tú de cuándo cambia el proxy que da acceso a todo lo demás.
Seguridad y Mejores Prácticas
Implementa estas recomendaciones para fortalecer tu instalación:
- Activa headers de seguridad: Incluye HSTS, X-Frame-Options y CSP
- Limita tasas de peticiones: Protege contra ataques DDoS básicos
- Usa redes Docker aisladas: Separa servicios públicos de bases de datos
- Monitoriza logs: Configura salida de logs para análisis
- Actualiza regularmente: Mantén la imagen Docker al día
Ejemplo de configuración segura:
ejemplo.com {
reverse_proxy backend:8080
header {
Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
Permissions-Policy "geolocation=(), microphone=(), camera=()"
}
# Rate limiting
rate_limit {
zone example_zone {
key {remote_host}
events 100
window 1m
}
}
}
Resolución de Problemas Comunes
Si encuentras errores durante el despliegue de Caddy Docker Compose, consulta esta guía de soluciones:
Error: No se puede obtener certificado SSL
Verifica que:
- Tu dominio apunta correctamente a la IP del servidor
- Los puertos 80 y 443 están accesibles desde Internet
- No hay otro servicio ocupando esos puertos
Error: 502 Bad Gateway
Este error indica que el backend no es accesible. Comprueba:
- El servicio backend está ejecutándose
- Ambos contenedores están en la misma red Docker
- El puerto especificado es correcto
Usa este comando para verificar conectividad:
docker exec caddy wget -O- http://backend:puerto
Error de permisos en volúmenes
Si encuentras errores de permisos, ajusta el ownership del directorio:
sudo chown -R 1000:1000 ./caddy_data ./caddy_config
Monitorización y Logs
Mantener visibilidad sobre tu servidor web es crucial. Configura logging detallado:
ejemplo.com {
reverse_proxy backend:80
log {
output file /data/access.log {
roll_size 100mb
roll_keep 10
roll_keep_for 720h
}
format json
level INFO
}
}
Para ver logs en tiempo real:
docker logs -f caddy
Integra con herramientas como Grafana y Loki para análisis avanzado de métricas.
Despliegue en Producción
Al llevar tu configuración a producción, considera estos aspectos:
- Backups automáticos: Respalda el volumen caddy_data que contiene certificados
- Health checks: Implementa comprobaciones de salud en Docker Compose
- Resource limits: Define límites de CPU y memoria
- Staging environment: Prueba cambios antes de aplicarlos en producción
Ejemplo de configuración para producción:
version: '3.8'
services:
caddy:
image: caddy:2-alpine
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- web
deploy:
resources:
limits:
cpus: '2'
memory: 512M
reservations:
cpus: '0.5'
memory: 128M
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:2019/config/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
volumes:
caddy_data:
driver: local
caddy_config:
networks:
web:
driver: bridge
Preguntas Frecuentes (FAQ)
¿Necesito conocimientos avanzados para usar Caddy Docker Compose?
No, la configuración es mucho más sencilla que otros servidores web. Con conocimientos básicos de Docker y YAML puedes tener tu servidor funcionando en minutos. La sintaxis del Caddyfile es intuitiva y la documentación es excelente.
¿Es seguro para entornos de producción?
Absolutamente. El proyecto tiene más de 68,000 estrellas en GitHub y gestiona millones de certificados TLS en producción. Su seguridad está probada y es utilizado por empresas alrededor del mundo.
¿Puedo usar Caddy con certificados SSL propios?
Sí, aunque la obtención automática es su punto fuerte, puedes especificar certificados personalizados usando las directivas tls en el Caddyfile con las rutas a tus archivos .crt y .key.
¿Cómo actualizo Caddy a la última versión?
Simplemente ejecuta estos comandos:
docker compose pull
docker compose up -d
Los volúmenes persistentes conservarán tus certificados y configuración.
¿Funciona en ARM/Raspberry Pi?
Sí, las imágenes oficiales son multi-arquitectura y funcionan en ARM32, ARM64 y x86_64. Es perfecto para homelabs y dispositivos de bajo consumo.
Conclusión
Implementar Caddy Docker Compose es la forma más sencilla de gestionar HTTPS automático y reverse proxy en 2025. Su sintaxis clara, configuración automática de SSL y soporte para HTTP/3 lo convierten en una opción superior para proyectos modernos.
Ya sea que estés montando un homelab, desplegando aplicaciones web o gestionando microservicios, esta solución ofrece la combinación perfecta de simplicidad y potencia. Con la configuración presentada en esta guía, tendrás un servidor web robusto y seguro funcionando en minutos.
¿Listo para comenzar? Descarga el ejemplo completo y empieza a desplegar tus servicios con HTTPS automático hoy mismo.
