Una estructura práctica y mantenible para desplegar varios proyectos pequeños en un mismo servidor.
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.
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.
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.
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.
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.
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.
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.
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.
0 vistas
Diseño plataformas escalables y optimizo infraestructura para que tu equipo avance con mayor rapidez, seguridad y claridad.
© 2026 Ronald Ramos. Todos los derechos reservados.