Docs

Deployment

Tabla de Contenidos

  1. Vision General del Pipeline
  2. Coolify VPS (DEV/TEST)
  3. Railway Pro (Produccion)
  4. Cloudflare Pages (Docs + Landing)
  5. CI/CD GitLab
  6. Dockerfiles y Multi-Stage Builds
  7. Rollback

1. Vision General del Pipeline

El deployment de Nvito combina multiples herramientas segun el ambiente y el tipo de servicio. El flujo general es: el desarrollador hace push, GitLab CI valida calidad, y el proveedor de hosting despliega automaticamente.

Matriz de Deployment

ProyectoDEV/TESTPRODTrigger
nvito-apiCoolify (Docker)Railway ProGitLab webhook / git push
nvito-adminCoolify (Docker)Railway ProGitLab webhook / git push
nvito-invitationsCoolify (Docker)Railway ProGitLab webhook / git push
nvito-pwaCoolify (Docker)Railway ProGitLab webhook / git push
nvito-docsCloudflare Pagesgit push a main
Landing (nvito.mx)Cloudflare Pagesgit push a main

2. Coolify VPS (DEV/TEST)

Coolify v4 es un PaaS open-source que gestiona contenedores Docker en el VPS de Contabo. Funciona como un "Heroku self-hosted".

2.1 Infraestructura del VPS

AspectoDetalle
ProveedorContabo Cloud VPS 10
Specs4 vCPU, 8 GB RAM, 150 GB SSD
RegionUnited States (Central)
OSUbuntu 22.04 LTS
Costo$6.15 USD/mes ($4.95 + $1.20 region)
IP212.28.185.197
Reverse ProxyTraefik (integrado en Coolify)
SSLLet's Encrypt (automatico via Traefik)

2.2 Servicios Desplegados

ServicioAmbienteBranchDominioPuerto
nvito-api-devDEVdevelopdev-api.nvito.mx3000
nvito-api-testTESTtesttest-api.nvito.mx3000
nvito-admin-devDEVdevelopdev-admin.nvito.mx5050
nvito-admin-testTESTtesttest-admin.nvito.mx5050
nvito-invitations-devDEVdevelopdev-inv.nvito.mx3001
nvito-invitations-testTESTtesttest-inv.nvito.mx3001
nvito-pwa-devDEVdevelopdev-app.nvito.mx3002
nvito-pwa-testTESTtesttest-app.nvito.mx3002

2.3 Flujo de Deploy en Coolify

2.4 Conexion con GitLab

Coolify v4 no tiene integracion nativa con GitLab (solo GitHub y Gitea). La conexion se hace via deploy keys SSH:

  1. Generar SSH key en Coolify: Security > Private Keys > Add
  2. Copiar la clave publica al repo en GitLab: Settings > Repository > Deploy Keys
  3. Configurar servicio en Coolify como "Private Repository (with Deploy Key)"
  4. Configurar webhook URL de Coolify en GitLab: Settings > Webhooks > Push events

2.5 Variables de Entorno en Coolify

Las variables se configuran directamente en el dashboard de Coolify por servicio. No se usan archivos .env ni BWS para el runtime de las aplicaciones en Coolify.

Variables NEXT_PUBLIC_* en build time

Las variables con prefijo NEXT_PUBLIC_ se inyectan en build time (durante docker build). Si se modifican en Coolify, es necesario hacer un redeploy (rebuild completo) para que tomen efecto. Las variables de servidor (sin prefijo) se leen en runtime y toman efecto al reiniciar el contenedor.

2.6 SSL y Dominios

Traefik gestiona SSL automaticamente:

  1. Coolify configura labels de Traefik en el contenedor con el dominio custom
  2. Traefik detecta el nuevo dominio y solicita certificado Let's Encrypt via ACME HTTP-01
  3. El certificado se renueva automaticamente 30 dias antes de expirar
  4. Cloudflare proxy (modo Full) confía en el certificado Let's Encrypt del origin

2.7 Sin Suspension

Docker en VPS no duerme los contenedores. Esto es critico porque nvito-api usa Bull queues con Redis para procesar emails, WhatsApp y jobs asincronos. Si el backend se suspende, los jobs en cola no se procesan.

3. Railway Pro (Produccion)

Railway Pro es la plataforma de hosting planificada para produccion. Ofrece deployment automatico, private networking y escalado vertical sin gestion de servidores.

3.1 Configuracion del Proyecto

3.2 Auto-Deploy desde GitLab

Railway soporta deployment automatico conectando el repositorio de GitLab:

  1. Vincular repo GitLab al servicio Railway
  2. Configurar branch de deploy: main
  3. Cada push a main dispara build + deploy automatico
  4. Railway detecta el Dockerfile y ejecuta multi-stage build
  5. Health check antes de cortar trafico al contenedor anterior

3.3 Custom Domains y CNAME

Para cada servicio en Railway se configura un custom domain que apunta via CNAME:

app.nvito.mx      CNAME    nvito-pwa-prod.up.railway.app
inv.nvito.mx      CNAME    nvito-inv-prod.up.railway.app
admin.nvito.mx    CNAME    nvito-admin-prod.up.railway.app

Para api.nvito.mx, el CNAME apunta al Cloudflare Worker (no directamente a Railway).

3.4 Health Checks

ServicioEndpointIntervaloTimeoutRetries
nvito-apiGET /health10s5s3
nvito-adminGET /10s5s3
nvito-invitationsGET /10s5s3
nvito-pwaGET /10s5s3

Si el health check falla despues de 3 retries, Railway revierte al deployment anterior automaticamente.

3.5 Pricing Railway Pro

ConceptoCosto
Plan Pro$5/usuario/mes
Compute (estimado)~$0.000463/vCPU/min + $0.000231/GB/min
PostgreSQLIncluido en compute
Networking$0.10/GB egress (100 GB gratis/mes)
Estimacion mensual$50-80 USD (6 servicios + PostgreSQL)

4. Cloudflare Pages (Docs + Landing)

Los sitios estaticos de Nvito (documentacion y landing page) se despliegan en Cloudflare Pages, aprovechando la red edge global y el deploy automatico.

4.1 Proyectos en CF Pages

ProyectoRepositorioBuild CommandOutput DirCustom Domain
nvito-docsnvito-docsnpm run build.next/standalonedocs.nvito.mx
nvito-landingnvito-landingnpm run buildoutnvito.mx

4.2 Configuracion de Build

4.3 Configuracion en Pages Dashboard

SettingValor
Framework presetNext.js (Static Export)
Node.js version20
Build commandnpm run build
Build output directory.next/standalone (docs) o out (landing)
Root directory/

4.4 Custom Domain + CF Access

Para docs.nvito.mx:

  1. Configurar custom domain en CF Pages dashboard
  2. CF agrega CNAME automaticamente: docs.nvito.mxnvito-docs.pages.dev
  3. Habilitar CF Access en el subdominio para restringir acceso

Preview deploys

Cloudflare Pages genera una URL de preview unica para cada commit y branch (ej. abc123.nvito-docs.pages.dev). Esto permite revisar cambios antes de merge a main sin afectar produccion.

5. CI/CD GitLab

5.1 Pipeline Stages

5.2 Stage Quality

Se ejecuta en cada push a develop, test, main y en merge requests. Contiene 2 jobs paralelos:

Job lint:

lint:
  stage: quality
  script:
    - npm ci --cache $NPM_CONFIG_CACHE
    - npm run lint
    - npm run format:check

Job test:unit:

test:unit:
  stage: quality
  services:
    - postgres:15-alpine
  variables:
    POSTGRES_DB: nvito_test
    DATABASE_URL: "postgresql://test:test@postgres:5432/nvito_test"
  script:
    - npm ci --cache $NPM_CONFIG_CACHE
    - npx prisma generate
    - npx prisma migrate deploy
    - npm run test:ci
  artifacts:
    reports:
      junit: junit.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
    expire_in: 30 days

5.3 Stage Migrate

Se ejecuta solo cuando hay cambios en prisma/migrations/**/*. Usa Bitwarden Secrets Manager (BWS) para obtener la DATABASE_URL del ambiente correspondiente.

5.4 Estrategia de Branching

RamaAmbienteDeployMigraciones
feature/*NoNo
developDEVAuto (Coolify)Si (Neon DEV)
testTESTAuto (Coolify)Si (Neon TEST)
mainPRODAuto (Railway)Si (Railway PG)

Flujo:

feature/xyz → MR a develop → merge → deploy DEV
develop → merge a test → deploy TEST
test → merge a main → deploy PROD

5.5 Variables de CI/CD en GitLab

VariableAlmacenada enProtectedMasked
BWS_ACCESS_TOKENGitLab CI/CDSiSi
BWS_PROJECT_DEVGitLab CI/CDNoNo
BWS_PROJECT_TESTGitLab CI/CDNoNo
BWS_PROJECT_PRODGitLab CI/CDSiNo

Todos los demas secretos (DATABASE_URL, API keys, tokens) se almacenan en Bitwarden Secrets Manager y se inyectan via bws run en runtime.

5.6 Estado Actual

Pipelines deshabilitados temporalmente

Los pipelines de GitLab CI estan deshabilitados temporalmente en los 5 proyectos (workflow.rules: never) para ahorrar compute de shared runners. Los tests y validaciones se ejecutan localmente antes de cada push. Se reactivaran al escalar el equipo.

6. Dockerfiles y Multi-Stage Builds

Todos los proyectos de Nvito usan Dockerfiles multi-stage para producir imagenes minimas y seguras.

6.1 Patron Comun (3 Stages)

6.2 Ejemplo: nvito-api Dockerfile

# Stage 1: Install dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts

# Stage 2: Build application
FROM node:20-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npx prisma generate
RUN npm run build
RUN npm prune --production

# Stage 3: Production runtime
FROM node:20-alpine AS runtime
RUN apk add --no-cache dumb-init
WORKDIR /app
USER node

COPY --from=build --chown=node:node /app/dist ./dist
COPY --from=build --chown=node:node /app/node_modules ./node_modules
COPY --from=build --chown=node:node /app/prisma ./prisma
COPY --from=build --chown=node:node /app/package.json ./

EXPOSE 3000
CMD ["dumb-init", "node", "dist/main.js"]

6.3 Ejemplo: nvito-admin Dockerfile (Next.js Standalone)

# Stage 1: Dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

# Stage 2: Build
FROM node:20-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# NEXT_PUBLIC_* variables must be available at build time
ARG NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=$NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
RUN npm run build

# Stage 3: Runtime (standalone output)
FROM node:20-alpine AS runtime
RUN apk add --no-cache dumb-init
WORKDIR /app
USER node

COPY --from=build --chown=node:node /app/.next/standalone ./
COPY --from=build --chown=node:node /app/.next/static ./.next/static
COPY --from=build --chown=node:node /app/public ./public

EXPOSE 5050
ENV PORT=5050
CMD ["dumb-init", "node", "server.js"]

6.4 Buenas Practicas en los Dockerfiles

PracticaRazon
node:20-alpineImagen base minima (~50 MB vs ~350 MB de node:20)
dumb-initManejo correcto de senales POSIX (SIGTERM, SIGINT) para graceful shutdown
USER nodeEjecutar como usuario no-root para seguridad
Multi-stageImagen final no contiene devDependencies, source code ni herramientas de build
COPY selectivoSolo copiar artefactos necesarios para runtime
npm ciInstalacion reproducible desde lockfile
--ignore-scriptsPrevenir ejecucion de scripts postinstall no confiables en stage deps

7. Rollback

Cada plataforma de hosting ofrece mecanismos de rollback en caso de deploy fallido.

7.1 Railway

MetodoComoVelocidad
DashboardDeployments > seleccionar version anterior > RedeploySegundos
CLIrailway up --detach --service {id} --commit {sha}Segundos
Git revertgit revert HEAD && git push → auto-deployMinutos (requiere build)

Railway mantiene historial de todos los deployments. El rollback via dashboard es instantaneo porque reutiliza la imagen Docker ya construida.

7.2 Coolify

MetodoComoVelocidad
DashboardService > Deployments > Restart con commit anteriorSegundos
ManualSSH al VPS, docker ps, rollback manual con imagen anteriorMinutos
Git revertgit revert HEAD && git push → webhook rebuildMinutos

7.3 Cloudflare Pages

MetodoComoVelocidad
DashboardDeployments > seleccionar version > Rollback to this deploySegundos
AutomaticoCada commit genera un deployment inmutable con URL unica

CF Pages mantiene todos los deployments historicos como snapshots inmutables. El rollback es instantaneo.

7.4 Base de Datos

Rollback de migraciones requiere cuidado

A diferencia del codigo, las migraciones de base de datos no se pueden revertir automaticamente. prisma migrate deploy aplica migraciones hacia adelante unicamente. Para revertir un cambio de schema, se debe crear una nueva migracion que deshaga los cambios.

EscenarioSolucion
Migracion fallida (no completo)Prisma marca la migracion como failed. Corregir el SQL y ejecutar prisma migrate resolve --applied {name}
Migracion exitosa pero codigo incompatibleCrear nueva migracion que revierta los cambios de schema
Perdida de datosRestaurar desde backup (Neon: branching point-in-time, Railway: snapshots automaticos)

7.5 Diagrama de Decision de Rollback

Esta pagina fue util?