Express.js
Express en Docker
Explica cómo construir imágenes reproducibles y mínimas para Express.js con multi-stage builds, usuario no root, señales, health checks y configuración runtime.
- Última actualización
- Actualizada
- Nivel
- Aplicación
Express.js
Explica cómo construir imágenes reproducibles y mínimas para Express.js con multi-stage builds, usuario no root, señales, health checks y configuración runtime.
Docker empaqueta el proceso y sus archivos; no reemplaza configuración, seguridad, health checks ni operación. Una imagen buena es pequeña, reproducible y ejecuta con mínimos privilegios.
source + lockfile
↓ build stage
JavaScript compilado + prod dependencies
↓ runtime stage
imagen inmutableFROM node:24-bookworm-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY tsconfig.json ./
COPY src ./src
RUN npm run typecheck && npm run build && npm prune --omit=dev
FROM node:24-bookworm-slim AS runtime
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build /app/package.json ./
COPY --from=build /app/package-lock.json ./
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]La versión debe ajustarse a la rama LTS soportada por el proyecto. Fijar digest mejora reproducibilidad, pero requiere actualización planificada.
node_modules
.git
.env*
coverage
dist
*.logEvita enviar secretos y archivos innecesarios al build context.
npm ci respeta lockfile. No copies node_modules del host: puede contener binarios para otro sistema.
Después del build, conserva solo dependencias de producción. Verifica que herramientas necesarias en runtime no estén clasificadas incorrectamente como dev.
USER node limita impacto. También configura permisos de archivos antes de cambiar usuario.
No uses root solo porque un volumen o puerto falla; corrige ownership o escucha en puerto no privilegiado.
Usa exec form:
CMD ["node", "dist/server.js"]Así Node recibe SIGTERM directamente. Shell form puede interceptar señales.
Un init ligero puede ayudar a recolectar procesos hijos si la app los crea, pero no siempre es necesario.
No copies .env ni secrets en imagen. Inyecta:
docker run --env-file .env -p 3000:3000 app-imageEn producción usa el mecanismo seguro de la plataforma. Variables pueden ser visibles para procesos/operadores; aplica least privilege.
La app escucha 0.0.0.0, no solo localhost (se abre en otra pestaña):
server.listen(port, '0.0.0.0');EXPOSE documenta el puerto; no lo publica. -p o Service/Ingress lo hace accesible.
Docker puede definir:
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD node -e "fetch('http://127.0.0.1:3000/health/live').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"En Kubernetes suelen usarse probes externas en vez del HEALTHCHECK de la imagen. No dupliques políticas contradictorias.
Contenedores son efímeros. No guardes uploads o datos críticos localmente. Usa object storage o volúmenes cuando el requisito sea persistencia.
Un filesystem read-only reduce superficie. Proporciona directorios temporales explícitos si la app los necesita.
Compatibilidad amplia con binarios nativos y herramientas conocidas.
Más pequeña, usa musl y puede complicar dependencias nativas. No es automáticamente más segura.
Runtime mínimo, menos shell y debugging interactivo. Requiere buena observabilidad y build correcto.
Elige según compatibilidad y operación, no solo tamaño.
Paquetes como bcrypt, sharp o drivers pueden compilar addons. Build y runtime deben ser compatibles. Multi-stage con bases distintas (glibc/musl) puede romper.
Copia package/lock antes del source para reutilizar instalación cuando dependencias no cambian. BuildKit cache mounts pueden acelerar CI, pero el artifact final debe seguir reproducible.
Si un registry privado requiere token, usa secret mounts de BuildKit; no ARG NPM_TOKEN, porque puede quedar en metadata/capas.
Inclúyelos si se necesitan para stacks, evitando publicar source sensible sin control. Configura --enable-source-maps o runtime según estrategia.
Guarda tiempos en UTC y evita depender de timezone del contenedor. Si necesitas ICU/locales completos, verifica que la imagen los incluya.
Docker envía SIGTERM y luego SIGKILL tras timeout. La app debe drenar antes. Configura stop_grace_period coherente con shutdown deadline.
Define límites de CPU/memoria en plataforma y prueba con ellos. Node puede detectar límites de contenedor en versiones modernas, pero observa heap/RSS y OOMKills.
Un contenedor sin límites puede afectar vecinos; uno demasiado pequeño reinicia bajo carga.
services:
api:
build: .
environment:
DATABASE_URL: postgresql://postgres:postgres@db:5432/app
ports:
- "3000:3000"
depends_on:
db:
condition: service_healthy
db:
image: postgres:18depends_on no reemplaza retries/readiness de la app. El ejemplo debe fijar versiones y volúmenes según proyecto.
Pipeline:
.env, source innecesario o dev dependencies.npm install sin lock.Escalado de aplicaciones Express analiza qué ocurre al ejecutar múltiples instancias y dónde permanece el estado.