Docker
Zentto usa Docker para empaquetar la API y las micro-apps del frontend. La arquitectura se compone de dos imagenes principales y un reverse proxy Nginx.
Dockerfile.api — API Node.js
Build multi-stage con node:20-alpine. La primera etapa compila TypeScript y la segunda copia solo el resultado de produccion.
# docker/Dockerfile.api
FROM node:20-alpine AS builder
WORKDIR /app
COPY web/api/package.json web/api/package-lock.json web/api/.npmrc ./
RUN npm ci
COPY web/api/ .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./
COPY --from=builder /app/package-lock.json ./
COPY --from=builder /app/.npmrc ./
COPY web/contracts/openapi.yaml ./contracts/openapi.yaml
RUN npm ci --omit=dev
RUN mkdir -p /app/storage/media
EXPOSE 4000
CMD ["node", "dist/index.js"] Dockerfile.frontend — Next.js + PM2
Tambien multi-stage. La etapa de build compila las 15 micro-apps con npm run build:all. Las variables NEXT_PUBLIC_* se bakean en build-time via ARGs.
# docker/Dockerfile.frontend (resumen)
FROM node:20-alpine AS builder
ARG NEXT_PUBLIC_API_BASE_URL=http://localhost:4000
ARG NEXT_PUBLIC_SHELL_URL=http://localhost:3000
# ... mas ARGs
COPY web/modular-frontend/ .
RUN npm ci && npm run build:all
FROM node:20-alpine AS runner
RUN npm install -g pm2
COPY --from=builder /app/apps ./apps
COPY --from=builder /app/packages ./packages
COPY --from=builder /app/node_modules ./node_modules
COPY docker/pm2.config.cjs /app/pm2.config.cjs
EXPOSE 3000-3014
CMD ["pm2-runtime", "/app/pm2.config.cjs"] PM2 — Ecosystem de procesos
El archivo docker/pm2.config.cjs define 15 procesos Next.js, cada uno en su puerto:
| App | Puerto | Tipo |
|---|---|---|
| shell | 3000 | Shell (auth, navegacion) |
| contabilidad | 3001 | Dentro del shell |
| nomina | 3002 | Dentro del shell |
| pos | 3003 | Sub-app independiente |
| bancos | 3004 | Dentro del shell |
| inventario | 3005 | Dentro del shell |
| ventas | 3006 | Sub-app independiente |
| compras | 3007 | Dentro del shell |
| restaurante | 3008 | Sub-app independiente |
| ecommerce | 3009 | Sub-app independiente |
| auditoria | 3010 | Sub-app independiente |
| logistica | 3011 | Sub-app independiente |
| crm | 3012 | Sub-app independiente |
| manufactura | 3013 | Sub-app independiente |
| flota | 3014 | Sub-app independiente |
Las sub-apps independientes usan NEXT_BASE_PATH y SHELL_URL para redirigir al shell cuando se necesita autenticacion.
docker-compose.yml — Desarrollo local
Para levantar el entorno local con build desde codigo fuente:
docker compose up --build Incluye tres servicios: api (puerto 4000), frontend (puertos 3000-3010) y nginx (puerto 80). Las variables de entorno se leen desde web/api/.env.
docker-compose.prod.yml — Produccion
Usa imagenes pre-construidas desde GitHub Container Registry:
# Imagenes de produccion
api: ghcr.io/zentto-erp/zentto-web/api:latest
frontend: ghcr.io/zentto-erp/zentto-web/frontend:latest
# Variables de entorno desde archivos en el servidor
env_file: /opt/zentto/.env.api
env_file: /opt/zentto/.env.frontend La red y el volumen de storage son externos (creados previamente en el servidor):
networks:
zentto-net:
external: true
name: zentto_zentto-net
volumes:
api_storage:
external: true
name: zentto_api_storage Red Docker y PostgreSQL
La red Docker usa el rango 172.18.0.0/16. Para que los contenedores accedan a PostgreSQL (que corre en el host), se requiere:
- UFW:
ufw allow from 172.18.0.0/16 to any port 5432 - pg_hba.conf:
host zentto_prod zentto_app 172.18.0.0/16 scram-sha-256 - PG_HOST: usar
172.18.0.1(gateway Docker, no localhost)
localhost, la API dentro del contenedor no puede alcanzar PostgreSQL del host. Siempre usar la IP del gateway Docker.