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¶
- Dockerfile — la receta
- .dockerignore — qué se excluye del contexto de build (evita copiar
node_moduleslocal,.env, etc.) - package.json, package-lock.json — fuente de verdad de las dependencias
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
.envestá excluido en.dockerignorey 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.