# NestJS API + Next.js web on galactus (standalone Docker, Portainer endpoint 3). # # Standalone port of deploy/jorgecuadros-app.stack.yml — see the header of # deploy/galactus/jorgecuadros-db.compose.yml for the Swarm keys plain compose # silently ignores. The one that matters most here: without # `restart: unless-stopped` neither service returns after a host reboot. # # Cross-stack traffic still goes over the HOST, not service DNS. db and minio # are separate Portainer stacks, so they are on separate compose networks and # their service names do not resolve from here. DATABASE_URL / S3_ENDPOINT must # name galactus's own address and the published port — exactly as on cubex # today. Do not "simplify" them to `mysql:3306`. # # The web image is NOT URL-baked: the browser's API origin is injected at # runtime from API_ORIGIN (apps/web/src/app/layout.tsx), so the same image works # for any deployment. APP_VERSION / GIT_SHA / BUILD_DATE come baked in from # build.yml and are surfaced at GET /version (api) and in the web footer. # # Keep in sync with deploy/jorgecuadros-app.stack.yml when either changes. services: api: image: git.mancinas.io/rmancinas/jorgecuadros-api:${APP_TAG:-latest} restart: unless-stopped # Stable handle for deploy/scripts/pre-migrate-backup.sh, which finds this # container by label to run mysqldump into the backup volume. A label # survives stack renames; the compose service name does not. labels: io.jorgecuadros.role: "api" environment: DATABASE_URL: ${DATABASE_URL:?DATABASE_URL must be set} SESSION_SECRET: ${SESSION_SECRET:?SESSION_SECRET must be set} WEB_ORIGIN: ${WEB_ORIGIN:?WEB_ORIGIN must be set} PORT: "3001" INGEST_DIR: /data/ingest BACKUP_DIR: /data/backups MIGRATION_ENV: prod S3_ENDPOINT: ${S3_ENDPOINT:?S3_ENDPOINT must be set} S3_BUCKET: ${S3_BUCKET:-jorgecuadros-documents} MINIO_ROOT_USER: ${MINIO_ROOT_USER:?MINIO_ROOT_USER must be set} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?MINIO_ROOT_PASSWORD must be set} ports: - "${API_PORT:-3001}:3001" volumes: # Uploaded Access files and DB backups. Named, so they survive every # redeploy — and so the pre-migrate dump the deploy takes is the same # file the "Operaciones" restore screen lists. - ingest_data:/data/ingest - backup_data:/data/backups healthcheck: test: ["CMD-SHELL", "wget -qO- http://localhost:3001/health || exit 1"] interval: 15s timeout: 5s retries: 10 start_period: 30s web: image: git.mancinas.io/rmancinas/jorgecuadros-web:${APP_TAG:-latest} restart: unless-stopped labels: io.jorgecuadros.role: "web" environment: # Public API URL the browser calls (injected at runtime, see layout.tsx). API_ORIGIN: ${API_ORIGIN:?API_ORIGIN must be set} ports: - "${WEB_PORT:-3000}:3000" depends_on: # Unlike Swarm — which ignores depends_on entirely — plain compose honours # this, so web waits for the API to pass its healthcheck. api: condition: service_healthy healthcheck: test: ["CMD-SHELL", "wget -qO- http://localhost:3000/ >/dev/null 2>&1 || exit 1"] interval: 15s timeout: 5s retries: 10 start_period: 30s volumes: ingest_data: backup_data: