¿Por qué elegir Caddy como reverse proxy en tu VPS?
La mayoría de los reverse proxies delegan la gestión TLS a una herramienta externa — Certbot, acme.sh o un script cron propio. Caddy integra este mecanismo de extremo a extremo: en cuanto se declara un nombre de dominio en el Caddyfile, contacta con Let's Encrypt, obtiene el certificado y lo renueva antes de que caduque, sin intervención manual. El resultado es una configuración que se lee en pocas líneas y se despliega de forma idéntica en todos tus VPS, sin estado externo que sincronizar.
Lo que Caddy aporta en producción
- HTTPS automático: Let's Encrypt ACME HTTP-01 activado en cuanto se declara un nombre de dominio — sin cron, sin plugin que instalar
- Sintaxis concisa: un bloque de cinco líneas reemplaza un vhost de Nginx de cuarenta líneas con su archivo de configuración de Certbot
- Recarga sin corte:
caddy reloadaplica el nuevoCaddyfilesin interrumpir las conexiones en curso - HTTP/2 y HTTP/3 (QUIC) por defecto: activados sin configuración adicional en todas las versiones 2.x
- JSON API: la configuración puede modificarse en caliente vía REST API, útil para entornos dinámicos o en contenedores
- Imagen Docker oficial ligera:
caddy:2-alpinepesa menos de 20 MB y cubre la mayoría de los casos de producción
Requisitos previos
Antes de empezar, verifica que tu VPS cumple los siguientes requisitos.
Recursos mínimos: 512 MB de RAM y 1 vCPU son suficientes para Caddy solo. Cuenta con 1 GB de RAM y 2 vCPU si alojas varias aplicaciones detrás.
Puertos 80 y 443 libres: Caddy los ocupa para ACME HTTP-01 (puerto 80) y para el tráfico HTTPS (puerto 443). Comprueba que ningún otro proceso los esté usando:
ss -tlnp | grep -E ':80|:443'Nombre de dominio apuntando al VPS: Let's Encrypt valida la propiedad del dominio consultando el puerto 80 de tu IP. Tu registro DNS A debe estar activo y propagado antes de arrancar Caddy.
Sistema operativo: Debian 11/12 o Ubuntu 22.04/24.04. Los comandos de esta guía están probados en estas distribuciones.
Docker (opcional): si despliegas Caddy en un contenedor, se necesitan Docker Engine 24+ y Docker Compose v2.
Desplegar Caddy como reverse proxy
Método 1 — Instalación desde el repositorio APT oficial
Caddy ofrece un repositorio APT firmado. Añádelo e instala el paquete:
apt install -y debian-keyring debian-archive-keyring apt-transport-https curl curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \ | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \ | tee /etc/apt/sources.list.d/caddy-stable.list apt update && apt install caddyVerifica la versión instalada:
caddy versiondebe mostrarv2.9.xo superior.Método 2 — Despliegue con Docker Compose
Si tus aplicaciones ya corren con Docker, añadir un servicio Caddy al mismo
compose.ymles el enfoque más sencillo. Crea un archivocompose.ymlen la raíz de tu proyecto:services: caddy: image: caddy:2-alpine restart: unless-stopped ports: - "80:80" - "443:443" - "443:443/udp" volumes: - ./Caddyfile:/etc/caddy/Caddyfile - caddy_data:/data - caddy_config:/config networks: - proxy volumes: caddy_data: caddy_config: networks: proxy: external: trueCrea la red compartida una sola vez:
docker network create proxy. Tus otros servicios se unen a esta red para ser accesibles por Caddy.Crear el Caddyfile básico
El
Caddyfilees el archivo de configuración central. Para una instalación APT se encuentra en/etc/caddy/Caddyfile. Para Docker, colócalo junto a tucompose.yml.Ejemplo mínimo que expone una aplicación en el puerto 3000:
myapp.com { reverse_proxy localhost:3000 }Eso es todo. Caddy detecta que
myapp.comes un nombre de dominio, contacta con Let's Encrypt vía ACME HTTP-01, obtiene un certificado y redirige automáticamente todo el tráfico HTTP a HTTPS. Los certificados se almacenan en~/.local/share/caddy/(APT) o en el volumencaddy_data(Docker).Configuración multi-aplicación (multi-vhost)
Alojar varias aplicaciones en el mismo VPS solo requiere bloques adicionales en el mismo archivo:
app1.tudominio.com { reverse_proxy localhost:3000 } app2.tudominio.com { reverse_proxy localhost:4000 } blog.tudominio.com { reverse_proxy localhost:8080 }Cada bloque obtiene su propio certificado de Let's Encrypt. Caddy gestiona las renovaciones de forma independiente para cada dominio, en paralelo, sin interrupción de servicio.
Aplicar el Caddyfile sin interrupción
Para APT, recarga la configuración sin cortar conexiones:
systemctl reload caddy # o si modificaste el archivo fuera de /etc/caddy/: caddy reload --config /ruta/al/CaddyfilePara Docker:
docker compose exec caddy caddy reload --config /etc/caddy/CaddyfileVerifica que la sintaxis es correcta antes de recargar:
caddy validate --config /etc/caddy/Caddyfile.Añadir cabeceras de seguridad y rate limiting
Caddy soporta cabeceras de seguridad nativas mediante la directiva
header. Aquí hay un bloque reforzado para una aplicación en producción:myapp.com { reverse_proxy localhost:3000 header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" X-Content-Type-Options "nosniff" X-Frame-Options "SAMEORIGIN" Referrer-Policy "strict-origin-when-cross-origin" -Server } log { output file /var/log/caddy/myapp.log { roll_size 10mb roll_keep 5 } } }Para el rate limiting, el módulo
caddy-ratelimitse instala conxcaddy buildy se usa con la directivarate_limit. La imagen oficial de Docker no lo incluye — construye tu propia imagen o usa un firewall de aplicaciones en capa superior si la necesidad es crítica.Activar y verificar el servicio systemd (APT)
Tras la instalación con APT, el servicio se activa automáticamente. Controla su estado y los registros:
systemctl status caddy journalctl -u caddy -fPara que Caddy arranque en cada reinicio:
systemctl enable caddy(ya hecho por el paquete). Comprueba que el certificado de Let's Encrypt se emitió correctamente:curl -sv https://myapp.com 2>&1 | grep -E 'subject|issuer|expire'
Cómo funciona el HTTPS automático de Caddy
Caddy implementa el protocolo ACME (Automated Certificate Management Environment) de forma nativa. En cuanto un bloque del Caddyfile contiene un nombre de dominio cualificado, Caddy activa automáticamente el desafío ACME HTTP-01: Let's Encrypt coloca un token de verificación en http://tudominio.com/.well-known/acme-challenge/, Caddy responde a esa petición y demuestra así que controla el servidor. Let's Encrypt emite entonces un certificado válido por 90 días.
Caddy renueva este certificado por adelantado (a partir de 30 días antes de su caducidad) y recarga la configuración en caliente, sin interrupción de servicio. Los certificados y los datos ACME se conservan localmente — en ~/.local/share/caddy/ para una instalación APT, en el volumen caddy_data para Docker. No elimines este volumen: Caddy también almacena ahí el estado ACME, y demasiadas peticiones a Let's Encrypt activan límites de velocidad que pueden bloquear la emisión de nuevos certificados durante varias horas.
Para certificados wildcard (*.tudominio.com), ACME HTTP-01 no funciona: se requiere el desafío DNS-01, que necesita acceso a la API de tu proveedor DNS. Caddy soporta nativamente varios proveedores mediante módulos (caddy-dns/cloudflare, caddy-dns/ovh…) compilados con xcaddy.
Caddy vs Nginx: cuándo elegir uno u otro
Desplace la tabla
| Criterio | Caddy | Nginx |
|---|---|---|
| Configuración TLS | Automática, cero configuración manual | Manual (Certbot o acme.sh requeridos) |
| Curva de aprendizaje | Baja — sintaxis legible, pocas directivas | Moderada — sintaxis verbosa, muchos contextos |
| Rendimiento bruto (req/s) | Excelente para la mayoría de cargas | Ligeramente superior bajo carga muy alta |
| Módulos y ecosistema | Creciente, xcaddy para compilar módulos | Muy amplio, numerosos módulos de terceros maduros |
| Recarga sin interrupción | Nativo (`caddy reload`) | Nativo (`nginx -s reload`), similar |
| Caso de uso ideal | VPS personal, proyectos de equipo, microservicios Docker | Grandes plataformas, frontal CDN, casos avanzados (GeoIP, Lua…) |
| Soporte HTTP/3 (QUIC) | Activado por defecto desde Caddy 2.6 | Experimental, requiere compilación con quiche o ngx_http_v3 |
Solución de problemas: errores comunes
Puerto 80 ya en uso. Caddy necesita el puerto 80 para ACME HTTP-01. Si Apache o Nginx ya están corriendo, detenerlos antes de arrancar Caddy: systemctl stop apache2 o systemctl stop nginx. Verifica después con ss -tlnp | grep :80.
Fallo ACME / certificado no emitido. Let's Encrypt no puede contactar con tu servidor. Comprueba: (1) tu registro DNS A apunta a la IP del VPS — dig +short tu-dominio.com debe devolver tu IP; (2) el puerto 80 es accesible desde el exterior — los firewalls del VPS (iptables, ufw) a veces bloquean este puerto aunque Caddy lo esté escuchando. Consulta los logs de Caddy con journalctl -u caddy para leer el mensaje de error ACME preciso.
permission denied en los puertos 80/443. Los puertos inferiores a 1024 están reservados en Linux. Para una instalación APT, el paquete configura automáticamente la capacidad CAP_NET_BIND_SERVICE en el binario de Caddy. Si compilaste Caddy manualmente, aplícala: setcap cap_net_bind_service=+ep $(which caddy).
DNS no resuelto / no such host. Caddy resuelve los nombres de los backends declarados en reverse_proxy. Si tu aplicación se llama app en Docker Compose, asegúrate de que Caddy y la aplicación comparten la misma red Docker. Verifica con: docker compose exec caddy nslookup app.
Volumen caddy_data eliminado por error. Caddy debe renegociar todos los certificados desde cero. Let's Encrypt impone un límite de 5 certificados por dominio en 7 días. Si alcanzas este límite, usa el entorno de staging ACME (acme_ca https://acme-staging-v02.api.letsencrypt.org/directory en el Caddyfile) para pruebas, y vuelve a producción una vez que haya pasado la ventana.
Para alojar múltiples aplicaciones en el mismo VPS
Crea una red Docker compartida (docker network create proxy) y conecta cada servicio a esa red. El Caddyfile referencia entonces los servicios por su nombre de contenedor en lugar de localhost:PORT — la configuración no cambia al añadir o quitar aplicaciones. Ejemplo: reverse_proxy mi-servicio:3000 en lugar de reverse_proxy localhost:3000.
Documentación oficial
La referencia completa de directivas, módulos y JSON API está disponible en <a href="https://caddyserver.com/docs/">caddyserver.com/docs</a>. El foro de la comunidad (<a href="https://caddy.community">caddy.community</a>) es el mejor recurso para preguntas avanzadas — los mantenedores responden directamente.