On a shared Docker network (Dokploy) plain service names like web or api also resolve to other stacks' containers, so the gateway balanced requests across foreign containers and returned 502. Services reached by name now carry an imen-* alias, used by the gateway upstreams and the service URLs. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
162 lines
4.7 KiB
YAML
162 lines
4.7 KiB
YAML
# Production stack. The images only copy pre-built output (`pnpm build`, committed), nothing is
|
|
# installed or compiled on the server. Copy .env.example to .env, fill in the secrets, then:
|
|
# docker compose up -d --build
|
|
# docker compose exec api node dist/seed/seed.js # first run only: initial content + admin user
|
|
#
|
|
# gateway (nginx) ─┬─ site domain → web (Nuxt SSR) /api → api /media → minio
|
|
# └─ admin domain → admin (static) /api → api /media → minio
|
|
|
|
# services that other containers reach by name get an imen-* alias: on a shared network (Dokploy) plain
|
|
# names like web or api would also resolve to other stacks' containers
|
|
#
|
|
# fixed project name, so this stack never shares containers or volumes with docker-compose.dev.yml
|
|
name: imen
|
|
|
|
services:
|
|
postgres:
|
|
image: docker.arvancloud.ir/postgres:18-alpine
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_USER: ${POSTGRES_USER}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
POSTGRES_DB: ${POSTGRES_DB}
|
|
volumes:
|
|
- pgdata:/var/lib/postgresql
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 10
|
|
|
|
minio:
|
|
image: docker.arvancloud.ir/minio/minio:latest
|
|
networks:
|
|
default:
|
|
aliases: [imen-minio]
|
|
restart: unless-stopped
|
|
command: server /data
|
|
environment:
|
|
MINIO_ROOT_USER: ${S3_ACCESS_KEY}
|
|
MINIO_ROOT_PASSWORD: ${S3_SECRET_KEY}
|
|
volumes:
|
|
- miniodata:/data
|
|
|
|
# runs pending database migrations, then exits; api starts after it succeeds
|
|
migrate:
|
|
image: imen-migrate
|
|
build:
|
|
context: .
|
|
dockerfile: docker/api.Dockerfile
|
|
target: migrate
|
|
restart: "no"
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
environment:
|
|
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
|
|
|
|
api:
|
|
image: imen-api
|
|
networks:
|
|
default:
|
|
aliases: [imen-api]
|
|
build:
|
|
context: .
|
|
dockerfile: docker/api.Dockerfile
|
|
target: runtime
|
|
restart: unless-stopped
|
|
depends_on:
|
|
migrate:
|
|
condition: service_completed_successfully
|
|
minio:
|
|
condition: service_started
|
|
environment:
|
|
NODE_ENV: production
|
|
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
|
|
JWT_SECRET: ${JWT_SECRET}
|
|
COOKIE_SECURE: ${COOKIE_SECURE:-true}
|
|
TRUST_PROXY: ${TRUST_PROXY:-2}
|
|
S3_ENDPOINT: http://imen-minio:9000
|
|
S3_BUCKET: ${S3_BUCKET:-imen-media}
|
|
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
|
|
S3_SECRET_KEY: ${S3_SECRET_KEY}
|
|
S3_FORCE_PATH_STYLE: "true"
|
|
S3_ENSURE_BUCKET: "true"
|
|
MEDIA_PUBLIC_URL: /media
|
|
WEB_REVALIDATE_URL: http://imen-web:3000/_internal/revalidate
|
|
REVALIDATE_SECRET: ${REVALIDATE_SECRET}
|
|
ADMIN_PHONE: ${ADMIN_PHONE}
|
|
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
|
|
ADMIN_NAME: ${ADMIN_NAME:-مدیر سایت}
|
|
|
|
web:
|
|
image: imen-web
|
|
networks:
|
|
default:
|
|
aliases: [imen-web]
|
|
build:
|
|
context: .
|
|
dockerfile: docker/web.Dockerfile
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- api
|
|
environment:
|
|
NUXT_API_INTERNAL_URL: http://imen-api:4000/api
|
|
NUXT_REVALIDATE_SECRET: ${REVALIDATE_SECRET}
|
|
NUXT_PUBLIC_SITE_URL: https://${SITE_DOMAIN}
|
|
|
|
admin:
|
|
image: imen-admin
|
|
networks:
|
|
default:
|
|
aliases: [imen-admin]
|
|
build:
|
|
context: .
|
|
dockerfile: docker/admin.Dockerfile
|
|
restart: unless-stopped
|
|
|
|
gateway:
|
|
image: docker.arvancloud.ir/nginx:alpine
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- web
|
|
- api
|
|
- admin
|
|
- minio
|
|
environment:
|
|
SITE_DOMAIN: ${SITE_DOMAIN}
|
|
ADMIN_DOMAIN: ${ADMIN_DOMAIN}
|
|
S3_BUCKET: ${S3_BUCKET:-imen-media}
|
|
volumes:
|
|
# rendered to /etc/nginx/conf.d/default.conf (replaces the stock server block)
|
|
- ./docker/nginx/gateway.conf.template:/etc/nginx/templates/default.conf.template:ro
|
|
ports:
|
|
- "${HTTP_PORT:-8080}:80"
|
|
|
|
# daily database dump into ./backups (kept 14 days)
|
|
backup:
|
|
image: docker.arvancloud.ir/postgres:18-alpine
|
|
restart: unless-stopped
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
environment:
|
|
PGHOST: postgres
|
|
PGUSER: ${POSTGRES_USER}
|
|
PGPASSWORD: ${POSTGRES_PASSWORD}
|
|
PGDATABASE: ${POSTGRES_DB}
|
|
volumes:
|
|
- ./backups:/backups
|
|
entrypoint:
|
|
- /bin/sh
|
|
- -c
|
|
- |
|
|
while true; do
|
|
pg_dump -Fc -f /backups/imen-$$(date +%F).dump && find /backups -name '*.dump' -mtime +14 -delete
|
|
sleep 86400
|
|
done
|
|
|
|
volumes:
|
|
pgdata:
|
|
miniodata:
|