fix(docker): Make port configuration consistent and dynamic
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Fixed the mismatch between environment variables:
- docker-compose now passes PORT (what NestJS/Next.js read) instead of API_PORT
- API_PORT/WEB_PORT control host mapping, PORT controls container
Changes:
- docker-compose: Pass PORT=${API_PORT} and PORT=${WEB_PORT} to containers
- docker-compose: Dynamic port mapping on both host and container sides
- docker-compose: Traefik labels use ${API_PORT}/${WEB_PORT} variables
- docker-compose: Healthchecks use PORT env var
- Dockerfiles: Removed hardcoded port values
- Dockerfiles: Healthchecks read PORT at runtime
This allows changing ports via API_PORT/WEB_PORT environment variables
and have all components (app, healthcheck, Traefik) use the correct port.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -80,12 +80,12 @@ WORKDIR /app/apps/api
|
|||||||
# Switch to non-root user
|
# Switch to non-root user
|
||||||
USER nestjs
|
USER nestjs
|
||||||
|
|
||||||
# Expose API port
|
# Expose API port (default 3001, can be overridden via PORT env var)
|
||||||
EXPOSE 3001
|
EXPOSE ${PORT:-3001}
|
||||||
|
|
||||||
# Health check
|
# Health check uses PORT env var (set by docker-compose or defaults to 3001)
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||||||
CMD node -e "require('http').get('http://localhost:3001/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
CMD node -e "const port = process.env.PORT || 3001; require('http').get('http://localhost:' + port + '/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
||||||
|
|
||||||
# Use dumb-init to handle signals properly
|
# Use dumb-init to handle signals properly
|
||||||
ENTRYPOINT ["dumb-init", "--"]
|
ENTRYPOINT ["dumb-init", "--"]
|
||||||
|
|||||||
@@ -90,17 +90,16 @@ WORKDIR /app/apps/web
|
|||||||
# Switch to non-root user
|
# Switch to non-root user
|
||||||
USER nextjs
|
USER nextjs
|
||||||
|
|
||||||
# Expose web port
|
# Expose web port (default 3000, can be overridden via PORT env var)
|
||||||
EXPOSE 3000
|
EXPOSE ${PORT:-3000}
|
||||||
|
|
||||||
# Environment variables
|
# Environment variables
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENV PORT=3000
|
|
||||||
ENV HOSTNAME="0.0.0.0"
|
ENV HOSTNAME="0.0.0.0"
|
||||||
|
|
||||||
# Health check
|
# Health check uses PORT env var (set by docker-compose or defaults to 3000)
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||||||
CMD node -e "require('http').get('http://localhost:3000', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
CMD node -e "const port = process.env.PORT || 3000; require('http').get('http://localhost:' + port, (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
||||||
|
|
||||||
# Use dumb-init to handle signals properly
|
# Use dumb-init to handle signals properly
|
||||||
ENTRYPOINT ["dumb-init", "--"]
|
ENTRYPOINT ["dumb-init", "--"]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
version: '3.9'
|
version: "3.9"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# ======================
|
# ======================
|
||||||
@@ -149,7 +149,15 @@ services:
|
|||||||
authentik-redis:
|
authentik-redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9000/-/health/live/"]
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"wget",
|
||||||
|
"--no-verbose",
|
||||||
|
"--tries=1",
|
||||||
|
"--spider",
|
||||||
|
"http://localhost:9000/-/health/live/",
|
||||||
|
]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 3
|
retries: 3
|
||||||
@@ -293,8 +301,8 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
# API Configuration
|
# API Configuration - PORT is what NestJS reads
|
||||||
API_PORT: ${API_PORT:-3001}
|
PORT: ${API_PORT:-3001}
|
||||||
API_HOST: ${API_HOST:-0.0.0.0}
|
API_HOST: ${API_HOST:-0.0.0.0}
|
||||||
# Database
|
# Database
|
||||||
DATABASE_URL: postgresql://${POSTGRES_USER:-mosaic}:${POSTGRES_PASSWORD:-mosaic_dev_password}@postgres:5432/${POSTGRES_DB:-mosaic}
|
DATABASE_URL: postgresql://${POSTGRES_USER:-mosaic}:${POSTGRES_PASSWORD:-mosaic_dev_password}@postgres:5432/${POSTGRES_DB:-mosaic}
|
||||||
@@ -311,14 +319,20 @@ services:
|
|||||||
# Ollama (optional)
|
# Ollama (optional)
|
||||||
OLLAMA_ENDPOINT: ${OLLAMA_ENDPOINT:-http://ollama:11434}
|
OLLAMA_ENDPOINT: ${OLLAMA_ENDPOINT:-http://ollama:11434}
|
||||||
ports:
|
ports:
|
||||||
- "${API_PORT:-3001}:3001"
|
- "${API_PORT:-3001}:${API_PORT:-3001}"
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
valkey:
|
valkey:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3001/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"]
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
'node -e "require(''http'').get(''http://localhost:${PORT:-3001}/health'', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"',
|
||||||
|
]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 3
|
retries: 3
|
||||||
@@ -334,7 +348,7 @@ services:
|
|||||||
- "traefik.http.routers.mosaic-api.rule=Host(`${MOSAIC_API_DOMAIN:-api.mosaic.local}`)"
|
- "traefik.http.routers.mosaic-api.rule=Host(`${MOSAIC_API_DOMAIN:-api.mosaic.local}`)"
|
||||||
- "traefik.http.routers.mosaic-api.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
|
- "traefik.http.routers.mosaic-api.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
|
||||||
- "traefik.http.routers.mosaic-api.tls=${TRAEFIK_TLS_ENABLED:-true}"
|
- "traefik.http.routers.mosaic-api.tls=${TRAEFIK_TLS_ENABLED:-true}"
|
||||||
- "traefik.http.services.mosaic-api.loadbalancer.server.port=3001"
|
- "traefik.http.services.mosaic-api.loadbalancer.server.port=${API_PORT:-3001}"
|
||||||
- "traefik.docker.network=${TRAEFIK_DOCKER_NETWORK:-mosaic-public}"
|
- "traefik.docker.network=${TRAEFIK_DOCKER_NETWORK:-mosaic-public}"
|
||||||
# Let's Encrypt (if enabled)
|
# Let's Encrypt (if enabled)
|
||||||
- "traefik.http.routers.mosaic-api.tls.certresolver=${TRAEFIK_CERTRESOLVER:-}"
|
- "traefik.http.routers.mosaic-api.tls.certresolver=${TRAEFIK_CERTRESOLVER:-}"
|
||||||
@@ -352,14 +366,21 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
|
PORT: ${WEB_PORT:-3000}
|
||||||
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:3001}
|
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:3001}
|
||||||
ports:
|
ports:
|
||||||
- "${WEB_PORT:-3000}:3000"
|
- "${WEB_PORT:-3000}:${WEB_PORT:-3000}"
|
||||||
depends_on:
|
depends_on:
|
||||||
api:
|
api:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"]
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
'node -e "require(''http'').get(''http://localhost:${PORT:-3000}'', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"',
|
||||||
|
]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 3
|
retries: 3
|
||||||
@@ -374,7 +395,7 @@ services:
|
|||||||
- "traefik.http.routers.mosaic-web.rule=Host(`${MOSAIC_WEB_DOMAIN:-mosaic.local}`)"
|
- "traefik.http.routers.mosaic-web.rule=Host(`${MOSAIC_WEB_DOMAIN:-mosaic.local}`)"
|
||||||
- "traefik.http.routers.mosaic-web.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
|
- "traefik.http.routers.mosaic-web.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
|
||||||
- "traefik.http.routers.mosaic-web.tls=${TRAEFIK_TLS_ENABLED:-true}"
|
- "traefik.http.routers.mosaic-web.tls=${TRAEFIK_TLS_ENABLED:-true}"
|
||||||
- "traefik.http.services.mosaic-web.loadbalancer.server.port=3000"
|
- "traefik.http.services.mosaic-web.loadbalancer.server.port=${WEB_PORT:-3000}"
|
||||||
- "traefik.docker.network=${TRAEFIK_DOCKER_NETWORK:-mosaic-public}"
|
- "traefik.docker.network=${TRAEFIK_DOCKER_NETWORK:-mosaic-public}"
|
||||||
# Let's Encrypt (if enabled)
|
# Let's Encrypt (if enabled)
|
||||||
- "traefik.http.routers.mosaic-web.tls.certresolver=${TRAEFIK_CERTRESOLVER:-}"
|
- "traefik.http.routers.mosaic-web.tls.certresolver=${TRAEFIK_CERTRESOLVER:-}"
|
||||||
|
|||||||
Reference in New Issue
Block a user