Reemplazando Nginx con Caddy en Docker Compose
Publicado el 2023-11-09 en Software
El servidor principal de mi homelab ejecuta varios servicios, pero el corazón de todo es una configuración de Docker Compose. Varios servicios están expuestos a Internet a través de un servidor web de proxy inverso.
Antes estaba usando tres imágenes de Docker para ejecutar Nginx, dirigir el tráfico a otros servicios y generar certificados Letsencrypt para los dominios necesarios:
- nginx con el servidor Nginx real en funcionamiento.
- nginx-proxy para generar configuraciones de Nginx.
- docker-letsencrypt-nginx-proxy-companion: para automatizar la provisión y renovación de certificados LetsEncrypt.
La parte relevante de la configuración en compose.yml lucía así:
services:
nginx:
image: nginx
labels:
com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: "true"
UFW_MANAGED: "true"
container_name: ${NGINX_WEB:-nginx}
restart: always
ports:
- "${DOCKER_HTTP:-80}:80"
- "${DOCKER_HTTPS:-443}:443"
volumes:
- ${NGINX_FILES_PATH}/conf.d:/etc/nginx/conf.d
- ${NGINX_FILES_PATH}/vhost.d:/etc/nginx/vhost.d
- ${NGINX_FILES_PATH}/html:/usr/share/nginx/html
- ${NGINX_FILES_PATH}/certs:/etc/nginx/certs:ro
- ${NGINX_FILES_PATH}/htpasswd:/etc/nginx/htpasswd:ro
logging:
driver: ${NGINX_WEB_LOG_DRIVER:-json-file}
options:
max-size: ${NGINX_WEB_LOG_MAX_SIZE:-4m}
max-file: ${NGINX_WEB_LOG_MAX_FILE:-10}
nginx-gen:
image: nginxproxy/docker-gen
command: -notify-sighup ${NGINX_WEB:-nginx} -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
container_name: ${DOCKER_GEN:-nginx-gen}
restart: always
volumes:
- ${NGINX_FILES_PATH}/conf.d:/etc/nginx/conf.d
- ${NGINX_FILES_PATH}/vhost.d:/etc/nginx/vhost.d
- ${NGINX_FILES_PATH}/html:/usr/share/nginx/html
- ${NGINX_FILES_PATH}/certs:/etc/nginx/certs:ro
- ${NGINX_FILES_PATH}/htpasswd:/etc/nginx/htpasswd:ro
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro
logging:
driver: ${NGINX_GEN_LOG_DRIVER:-json-file}
options:
max-size: ${NGINX_GEN_LOG_MAX_SIZE:-2m}
max-file: ${NGINX_GEN_LOG_MAX_FILE:-10}
nginx-letsencrypt:
image: nginxproxy/acme-companion
container_name: ${LETSENCRYPT_CONTAINER:-nginx-letsencrypt}
restart: always
volumes:
- ${NGINX_FILES_PATH}/conf.d:/etc/nginx/conf.d
- ${NGINX_FILES_PATH}/vhost.d:/etc/nginx/vhost.d
- ${NGINX_FILES_PATH}/html:/usr/share/nginx/html
- ${NGINX_FILES_PATH}/certs:/etc/nginx/certs:rw
- /var/opt/acme.sh:/etc/acme.sh
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
NGINX_DOCKER_GEN_CONTAINER: ${DOCKER_GEN:-nginx-gen}
NGINX_PROXY_CONTAINER: ${NGINX_WEB:-nginx}
logging:
driver: ${NGINX_LETSENCRYPT_LOG_DRIVER:-json-file}
options:
max-size: ${NGINX_LETSENCRYPT_LOG_MAX_SIZE:-2m}
max-file: ${NGINX_LETSENCRYPT_LOG_MAX_FILE:-10}
Para dirigir tráfico a un servicio sólo necesitaba definir las variables de entorno VIRTUAL_HOST
y LETSENCRYPT_HOST
.
Por ejemplo, esta era la configuración de mi servicio de Nextcloud (solo mostraré las partes relevantes):
services:
nextcloud:
image: nextcloud:latest
container_name: nextcloud
environment:
VIRTUAL_HOST: ${NEXTCLOUD_HOST}
LETSENCRYPT_HOST: ${NEXTCLOUD_HOST}
LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
Además de la configuración de servicios en Docker Compose, también necesitaba una plantilla de configuración de Nginx (ese es el nginx.tmpl que puedes ver en la configuración), y otros ficheros con ajustes específicos para algunos servicios (como por ejemplo aumentar el tamaño máximo de las peticiones HTTP para mi instancia de Nextcloud).
Ya había reemplazado el servidor web Nginx que aloja este blog por Caddy, así que quería hacer lo mismo con el servidor casero. Decidí usar caddy-docker-proxy porque era similar a nginx-proxy, por lo que simplificaría la migración. Lo que no me esperaba es cuán más simple se iba a volver.
La configuración anterior fue reemplazada por:
services:
caddy:
image: lucaslorentz/caddy-docker-proxy:ci-alpine
restart: unless-stopped
ports:
- "${DOCKER_HTTP:-80}:80"
- "${DOCKER_HTTPS:-443}:443"
networks:
- default
- webproxy
labels: # Opciones globales
caddy.email: ${CADDY_EMAIL}
UFW_MANAGED: "true"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# este volumen es necesario para mantener los certificados
# de lo contrario, se emitirán nuevos al reiniciar
- ${CADDY_FILES_PATH}:/data
Redirigir a un servicio ahora es solo cuestión de agregar algunas etiquetas:
services:
nextcloud:
image: nextcloud:latest
container_name: nextcloud
labels:
caddy: ${NEXTCLOUD_HOST}
caddy.reverse_proxy: "{{upstreams 80}}"
caddy.request_body.max_size: ${NEXTCLOUD_MAX_UPLOAD}
¡Y listo!
Caddy gestiona automáticamente los certificados TLS, así que no es necesario ningún servicio adicional. No puede ser más fácil.
También pude eliminar los archivos de configuración de Nginx, en su lugar los reemplacé con un par de etiquetas en los contenedores que requerían alguna configuración especial.
Antes de terminar este artículo, quiero destacar lo bien que nginx-proxy y su acme-companion habían estado funcionando durante años con un mantenimiento mínimo. Su estabilidad y compatibilidad hacia atrás fueron increíblemente sólidas, ¡mis felicidades y agradecimientos a su creador!