Porque elegirlo?
- usas Docker Compose para versionar tu despliegue;
- no quieres exponer directamente el puerto de tu aplicación;
- quieres una configuración más corta que una pila NGINX + Certbot tradicional.
No reemplaza a una arquitectura de alta disponibilidad ni a un balanceador administrado, pero es una excelente base para proyectos productivos simples.
Requisitos previos
Vas a necesitar:
- un Cloud Server con Ubuntu o Debian;
- acceso SSH con usuario
sudooroot; - Docker y Docker Compose instalados;
- un dominio o subdominio apuntando con registro
Aa la IP pública del servidor; - puertos
80/tcpy443/tcpabiertos en el firewall; - el puerto SSH habilitado solo para administración.
Importante: para validación HTTP-01 de Let's Encrypt, el puerto 80 debe estar accesible. Luego Caddy puede redirigir el tráfico a HTTPS.Paso 1: Preparar el servidor

Actualiza paquetes e instala herramientas básicas:
sudo apt updatesudo apt upgrade -ysudo apt install -y curl ca-certificates ufwConfigura el firewall local. Antes de activar UFW, asegúrate de permitir tu puerto SSH real:
sudo ufw allow OpenSSHsudo ufw allow 80/tcpsudo ufw allow 443/tcpsudo ufw enablesudo ufw statusSi usas el Firewall Virtual de DonWeb Cloud, abre también 80/tcp y 443/tcp desde el panel del Cloud Server. Si tu SSH usa un puerto personalizado, habilita ese puerto específico.
Paso 2: Crear la estructura del proyecto

Crea un directorio para el despliegue:
sudo mkdir -p /opt/mi-appsudo chown -R "$USER":"$USER" /opt/mi-appcd /opt/mi-appPara el ejemplo, vamos a usar una app Node.js mínima.
Crea package.json:
{ "name": "mi-app-cloud", "version": "1.0.0", "type": "module", "scripts": { "start": "node server.js" }, "dependencies": { "express": "^5.1.0" }}Crea server.js:
import express from "express";const app = express();const port = process.env.PORT || 3000;app.get("/", (req, res) => { res.json({ status: "ok", message: "Aplicacion publicada con Docker Compose y Caddy" });});app.listen(port, "0.0.0.0", () => { console.log(`App escuchando en puerto ${port}`);});Crea Dockerfile:
FROM node:22-alpineWORKDIR /appCOPY package*.json ./RUN npm install --omit=devCOPY server.js .ENV NODE_ENV=productionENV PORT=3000EXPOSE 3000USER nodeCMD ["npm", "start"]Paso 3: Configurar Docker Compose

Crea compose.yml:
services: app: build: . restart: unless-stopped environment: NODE_ENV: production PORT: 3000 networks: - web caddy: image: caddy:2-alpine restart: unless-stopped depends_on: - app ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config networks: - webnetworks: web:volumes: caddy_data: caddy_config:Observa que el servicio app no publica el puerto 3000 hacia Internet. Solo Caddy puede alcanzarlo dentro de la red Docker.
Paso 4: Configurar Caddy como proxy reverso
Crea Caddyfile y reemplaza app.tudominio.com por tu dominio real:
app.tudominio.com { encode zstd gzip reverse_proxy app:3000}
Con esta configuración, Caddy recibe tráfico público en 80 y 443, emite el certificado TLS y reenvía las solicitudes al contenedor app.

Paso 5: Levantar el despliegue
Construye y levanta los servicios:
docker compose up -d --buildVerifica el estado:
docker compose psdocker compose logs -f caddyPrueba desde tu equipo:
curl -I https://app.tudominio.comcurl https://app.tudominio.comSi todo está correcto, deberías recibir una respuesta HTTPS válida y el JSON de la aplicación.

Paso 6: Actualizar la aplicación
Cuando cambies código, reconstruye solo la app:
docker compose up -d --build appSi cambias el Caddyfile, recarga Caddy:
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile
Paso 7: Reforzar seguridad
Aplica estas recomendaciones antes de usar este esquema en producción:
- No publiques el puerto interno de la app con
ports. - Mantén abiertos hacia Internet solo
80,443y el puerto SSH necesario. - Usa variables de entorno seguras y no subas secretos al repositorio.
- Ejecuta backups si la app tiene base de datos o archivos persistentes.
- Actualiza imágenes periódicamente.
- Revisa logs después de cada deploy.
- Usa contraseñas robustas y, cuando sea posible, acceso SSH con llaves.
- Limita el acceso SSH por IP desde el Firewall Virtual si tu operación lo permite.

Problemas frecuentes
El certificado no se emite
Revisa que:
- el dominio apunte a la IP pública correcta;
- los puertos
80y443estén abiertos; - no haya otro servicio usando esos puertos;
- el
Caddyfiletenga el dominio real, no un placeholder.
La app funciona por Docker pero no por HTTPS
Verifica que el nombre del servicio en reverse_proxy app:3000 coincida con el servicio definido en compose.yml.
También puedes revisar logs:
docker compose logs appdocker compose logs caddyEl puerto 80 parece innecesario
Aunque todo el tráfico final use HTTPS, el puerto 80 sigue siendo importante para la validación HTTP-01 y para redirigir tráfico HTTP a HTTPS.
Conclusión
Docker Compose y Caddy forman una combinación muy cómoda para publicar aplicaciones en un Cloud Server: Compose ordena los servicios y Caddy simplifica el proxy reverso con HTTPS automático.
Si ya trabajas con contenedores, este flujo te permite pasar de una app local a un despliegue público con menos piezas manuales, manteniendo una configuración clara y versionable. En DonWeb Cloud puedes usar este enfoque sobre un Cloud Server con Docker, abrir solo los puertos necesarios y mantener el control de tu infraestructura.