Saltar a contenido

Dockerización del backend

Documenta cómo se construye la imagen del backend y por qué quedó así.

Imagen resultante

Atributo Valor
Base node:22.10-alpine
Tamaño final ~280 MB
Usuario nestjs (UID 1001, no-root)
Puerto interno 7000
Healthcheck GET /api/health cada 30s

Estrategia: multi-stage build

El Dockerfile tiene 4 stages:

Stage Función Por qué existe
deps Instala todas las deps con npm ci (incluye devDeps) Necesarias para compilar TypeScript
build Corre nest build → genera dist/ Compila el código
prod-deps Reinstala solo dependencies (sin dev) Imagen final más liviana
runner Imagen final con dist/ + prod node_modules Lo que se publica a GHCR

Sin multi-stage, la imagen final llevaría todas las devDependencies (~100MB extra) y código fuente.

Decisiones que pueden sorprender

1. NODE_OPTIONS=--openssl-legacy-provider

Está hardcodeado como ENV en el stage runner. Sin esto, NestJS arranca y crashea por incompatibilidad con OpenSSL 3 (default en Node 22). Es un workaround conocido. No tocar.

2. CMD apunta a dist/src/main y no a dist/main

NestJS por defecto escupe los artifacts en dist/main.js, pero este proyecto tiene archivos .ts fuera de src/ (configs, scripts), lo que fuerza a TypeScript a usar el ancestro común como rootDir. Resultado: el entry queda en dist/src/main.js. El CMD del Dockerfile refleja esto.

3. start-period=60s del healthcheck

El bootstrap del backend hace varias cosas síncronas al inicio (Redis, login a SAP, retries a Continental). Con menos de 60s, Docker marca el contenedor como unhealthy antes de que termine de levantarse. 60s es seguro.

4. EXPOSE 7000 y PORT=7000

El puerto interno coincide con el externo del host (7000:7000 en compose). Decisión deliberada — un solo número que recordar.

5. npm ci y no npm install

npm ci requiere que package-lock.json esté en sync con package.json. Si alguien edita package.json sin regenerar el lock, el build falla. Es lo que querés en CI: builds reproducibles.

Archivos clave

Build local (sin pipeline)

cd backend/
docker build -t oms-backend:dev .

No requiere ningún build arg ni env file. Toda la config del backend se inyecta en runtime.

Seguridad

  • Usuario no-root: el contenedor corre como nestjs (UID 1001). Si algo en el código permite ejecución de comandos, no puede tocar el filesystem del host.
  • Sin secretos en la imagen: el .env está excluido en .dockerignore y los secretos se inyectan al arrancar el contenedor.
  • Capas mínimas en el runner: solo wget (para healthcheck) además de Node y la app. Menos superficie de ataque.