Cómo organizo servicios con Docker y Nginx

Una estructura práctica y mantenible para desplegar varios proyectos pequeños en un mismo servidor.

AutorRonald RamosFecha9 ago. 2026Lectura4 min de lecturaEtiquetas

Cómo organizo servicios con Docker y Nginx

Alojar varios proyectos pequeños en un mismo servidor no exige construir una plataforma enorme, pero sí una estructura repetible. Mi objetivo es que desplegar una aplicación nueva sea predecible, que una incidencia pueda investigarse rápido y que cada servicio tenga límites claros.

La arquitectura base

Divido el servidor en cuatro capas: entrada, aplicaciones, datos y observabilidad. Nginx es el único punto público y termina TLS. Las aplicaciones se ejecutan en redes privadas; las bases de datos y colas nunca publican puertos hacia Internet.

Internet → Nginx → red proxy → aplicación
                              ↘ red privada → base de datos

Cada proyecto conserva su propio compose.yaml, archivo de variables, volúmenes y procedimiento de despliegue. Esto evita un archivo gigantesco donde una modificación accidental afecta a todos los sitios.

services:
  app:
    image: registry.example.com/peruanito:1.4.2
    restart: unless-stopped
    env_file: .env
    networks: [proxy, private]
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 5s
      retries: 3

networks:
  proxy:
    external: true
  private:
    internal: true

Fijar una versión concreta hace que el despliegue sea reproducible. latest parece cómodo hasta que dos servidores descargan imágenes distintas con el mismo nombre.

Nginx como puerta de entrada

Cada dominio tiene una configuración pequeña. Nginx centraliza certificados, tamaño máximo de solicitudes, cabeceras y límites de tiempo.

server {
    listen 443 ssl http2;
    server_name app.example.com;

    location / {
        proxy_pass http://peruanito-app:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

La aplicación debe confiar en los proxies conocidos, no en cualquier cabecera recibida. De lo contrario, un cliente podría falsificar IP o protocolo.

Despliegue y reversión

Mi secuencia habitual es: copia de seguridad si hay datos afectados, descarga de la versión, ejecución de pruebas, migraciones compatibles, reemplazo del contenedor y comprobación de salud. Mantengo la imagen anterior para poder volver rápidamente.

docker compose pull
docker compose run --rm app php artisan migrate --force
docker compose up -d --remove-orphans
docker compose ps

Las migraciones destructivas se separan en dos despliegues: primero el código que tolera ambas estructuras y después la eliminación. Así la reversión sigue siendo posible.

Convertir la secuencia en un script repetible

Ejecutar los comandos manualmente funciona hasta que hay presión, cansancio o varios servicios por actualizar. Preparé un script Bash reutilizable que recibe la carpeta del proyecto y, opcionalmente, la URL de salud:

./deploy-service.sh /srv/apps/peruanito https://app.example.com/health

El comienzo usa el modo estricto de Bash:

set -Eeuo pipefail

-e detiene el script cuando un comando falla; -u detecta variables no definidas; pipefail evita que un error dentro de una tubería quede oculto. -E permite que la captura de errores también se conserve dentro de funciones.

Antes de modificar contenedores, el script comprueba la carpeta, Docker, Compose y la existencia de un archivo de configuración. Después valida la configuración completa:

cd "$PROJECT_DIR"
docker compose config --quiet
docker compose pull
docker compose up -d --remove-orphans
docker compose ps

Esta validación no garantiza que la aplicación funcionará, pero evita iniciar un despliegue con YAML inválido o variables requeridas ausentes.

Verificar que levantar un contenedor no sea el final

docker compose up puede terminar correctamente aunque la aplicación todavía no responda. Cuando se proporciona una URL, el script ejecuta un health check con reintentos:

for ((attempt = 1; attempt <= MAX_ATTEMPTS; attempt++)); do
  if curl --fail --silent --show-error --max-time 10 "$HEALTH_URL" >/dev/null; then
    exit 0
  fi
  sleep "$RETRY_SECONDS"
done

Los valores se pueden ajustar sin editar el archivo:

MAX_ATTEMPTS=20 RETRY_SECONDS=3 \
  ./deploy-service.sh /srv/apps/peruanito https://app.example.com/health

Si una operación falla, una captura ERR muestra las últimas líneas de los logs. El script no ejecuta migraciones ni una reversión automática porque ambas operaciones dependen de cada aplicación. Prefiero que esas decisiones críticas sean explícitas y estén documentadas por proyecto.

Operación diaria

Los logs incluyen proyecto, entorno y solicitud. Las copias se prueban restaurándolas; una copia que nunca se restauró es sólo una esperanza. También vigilo espacio en disco, memoria, certificados y estados de salud.

Este modelo funciona bien para aplicaciones pequeñas y medianas: mantiene el costo operativo bajo sin sacrificar aislamiento, trazabilidad ni una ruta clara de crecimiento.

Descargar el script completo

Revísalo antes de ejecutarlo y adapta las rutas y el endpoint de salud a tu servidor. Después de descargarlo puedes conceder permiso de ejecución con chmod +x deploy-service.sh.

Descargar deploy-service.sh

¿Qué te pareció?

0 vistas

Comentarios (0)

Los comentarios se revisan antes de publicarse.

Contenido relacionado

Trabajemos juntos

Diseño plataformas escalables y optimizo infraestructura para que tu equipo avance con mayor rapidez, seguridad y claridad.

Iniciar una conversación

© 2026 Ronald Ramos. Todos los derechos reservados.

PrivacidadCookies