Compose file for ikerdemo.cat with nginx as TLS terminator, the three .NET services, Zitadel and the storage stack. Values come from deploy/.env.prod (untracked). Adds the frontend image, the migration script and the Zitadel provisioning for production.
274 lines
8.9 KiB
YAML
274 lines
8.9 KiB
YAML
name: musify-prod
|
|
|
|
# Production stack for the VPS (ikerdemo.cat). Everything runs on the internal
|
|
# `musify-prod_default` network; only nginx publishes ports to the host (80/443).
|
|
# Config in deploy/.env.prod.
|
|
#
|
|
# docker compose --env-file deploy/.env.prod -f deploy/docker-compose.prod.yml up -d --build
|
|
|
|
x-backend-depends: &backend-depends
|
|
postgres:
|
|
condition: service_healthy
|
|
rabbitmq:
|
|
condition: service_healthy
|
|
seaweedfs:
|
|
condition: service_healthy
|
|
|
|
x-backend-env: &backend-env
|
|
Database__ConnectionString: "Host=postgres;Port=5432;Database=${POSTGRES_DB};Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}"
|
|
MassTransit__Address: "rabbitmq://rabbitmq:5672"
|
|
MassTransit__Username: ${RABBITMQ_DEFAULT_USER}
|
|
MassTransit__Password: ${RABBITMQ_DEFAULT_PASS}
|
|
InfrastructureStorage__Address: "https://s3.ikerdemo.cat"
|
|
InfrastructureStorage__AccessKey: ${S3_ACCESS_KEY}
|
|
InfrastructureStorage__SecretAccessKey: ${S3_SECRET_KEY}
|
|
InfrastructureStorage__ForcePathStyle: "true"
|
|
InfrastructureStorage__UseHttp: "false"
|
|
ApplicationStorage__Bucket: ${S3_BUCKET}
|
|
OpenTelemetry__OtlpEndpoint: "http://jaeger:4317"
|
|
|
|
x-zitadel-db-env: &zitadel-db-env
|
|
ZITADEL_DATABASE_POSTGRES_HOST: postgres
|
|
ZITADEL_DATABASE_POSTGRES_PORT: 5432
|
|
ZITADEL_DATABASE_POSTGRES_DATABASE: zitadel
|
|
ZITADEL_DATABASE_POSTGRES_USER_USERNAME: zitadel
|
|
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: zitadel
|
|
ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE: disable
|
|
ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME: ${POSTGRES_USER}
|
|
ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD: ${POSTGRES_PASSWORD}
|
|
ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE: disable
|
|
|
|
services:
|
|
postgres:
|
|
image: postgres:17-alpine
|
|
container_name: musify-postgres
|
|
environment:
|
|
POSTGRES_USER: ${POSTGRES_USER}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
POSTGRES_DB: ${POSTGRES_DB}
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 10
|
|
restart: unless-stopped
|
|
|
|
zitadel-init:
|
|
image: ghcr.io/zitadel/zitadel:latest
|
|
container_name: musify-zitadel-init
|
|
command: 'init'
|
|
environment:
|
|
<<: *zitadel-db-env
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
restart: "no"
|
|
|
|
zitadel-setup:
|
|
image: ghcr.io/zitadel/zitadel:latest
|
|
container_name: musify-zitadel-setup
|
|
command: 'setup --masterkeyFromEnv --tlsMode disabled'
|
|
environment:
|
|
<<: *zitadel-db-env
|
|
ZITADEL_MASTERKEY: ${ZITADEL_MASTERKEY}
|
|
ZITADEL_EXTERNALSECURE: "true"
|
|
ZITADEL_EXTERNALDOMAIN: ${ZITADEL_EXTERNAL_DOMAIN}
|
|
ZITADEL_EXTERNALPORT: ${ZITADEL_EXTERNAL_PORT}
|
|
ZITADEL_TLS_ENABLED: "false"
|
|
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: ${ZITADEL_ADMIN_USERNAME}
|
|
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: ${ZITADEL_ADMIN_PASSWORD}
|
|
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED: "false"
|
|
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_USERNAME: zitadel-admin-sa
|
|
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_NAME: Admin Service Account
|
|
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_PAT_EXPIRATIONDATE: "2099-01-01T00:00:00Z"
|
|
ZITADEL_FIRSTINSTANCE_PATPATH: /output/admin-sa.pat
|
|
depends_on:
|
|
zitadel-init:
|
|
condition: service_completed_successfully
|
|
volumes:
|
|
- ./zitadel/.output:/output
|
|
restart: "no"
|
|
|
|
zitadel:
|
|
image: ghcr.io/zitadel/zitadel:latest
|
|
container_name: musify-zitadel
|
|
command: 'start --masterkeyFromEnv --tlsMode disabled'
|
|
environment:
|
|
<<: *zitadel-db-env
|
|
ZITADEL_MASTERKEY: ${ZITADEL_MASTERKEY}
|
|
ZITADEL_EXTERNALSECURE: "true"
|
|
ZITADEL_EXTERNALDOMAIN: ${ZITADEL_EXTERNAL_DOMAIN}
|
|
ZITADEL_EXTERNALPORT: ${ZITADEL_EXTERNAL_PORT}
|
|
ZITADEL_TLS_ENABLED: "false"
|
|
depends_on:
|
|
zitadel-setup:
|
|
condition: service_completed_successfully
|
|
volumes:
|
|
- ./zitadel/.output:/output
|
|
restart: unless-stopped
|
|
|
|
rabbitmq:
|
|
image: rabbitmq:4-management-alpine
|
|
container_name: musify-rabbitmq
|
|
environment:
|
|
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER}
|
|
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
|
|
volumes:
|
|
- rabbitmq_data:/var/lib/rabbitmq
|
|
healthcheck:
|
|
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 10
|
|
restart: unless-stopped
|
|
|
|
seaweedfs:
|
|
image: chrislusf/seaweedfs:latest
|
|
container_name: musify-seaweedfs
|
|
command: >
|
|
server
|
|
-ip.bind=0.0.0.0
|
|
-dir=/data
|
|
-master.volumeSizeLimitMB=1024
|
|
-volume.max=0
|
|
-filer
|
|
-s3
|
|
-s3.config=/etc/seaweedfs/s3.conf
|
|
volumes:
|
|
- seaweedfs_data:/data
|
|
- ./seaweedfs/s3.conf:/etc/seaweedfs/s3.conf:ro
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:9333/cluster/healthz"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 10
|
|
restart: unless-stopped
|
|
|
|
seaweedfs-init:
|
|
image: amazon/aws-cli:latest
|
|
container_name: musify-seaweedfs-init
|
|
depends_on:
|
|
seaweedfs:
|
|
condition: service_healthy
|
|
environment:
|
|
AWS_ACCESS_KEY_ID: ${S3_ACCESS_KEY}
|
|
AWS_SECRET_ACCESS_KEY: ${S3_SECRET_KEY}
|
|
AWS_DEFAULT_REGION: us-east-1
|
|
entrypoint: ["/bin/sh", "-c"]
|
|
command:
|
|
- |
|
|
aws --endpoint-url http://seaweedfs:8333 s3 mb s3://${S3_BUCKET} 2>/dev/null || true
|
|
aws --endpoint-url http://seaweedfs:8333 s3 ls
|
|
restart: "no"
|
|
|
|
jaeger:
|
|
image: jaegertracing/all-in-one:latest
|
|
container_name: musify-jaeger
|
|
environment:
|
|
COLLECTOR_OTLP_ENABLED: "true"
|
|
restart: unless-stopped
|
|
|
|
api:
|
|
build:
|
|
context: ../backend
|
|
dockerfile: Musify.Api.Dockerfile
|
|
container_name: musify-api
|
|
depends_on: *backend-depends
|
|
environment:
|
|
<<: *backend-env
|
|
ASPNETCORE_ENVIRONMENT: Production
|
|
ASPNETCORE_URLS: http://+:5111
|
|
StreamGateway__PublicBaseUrl: "https://${STREAM_DOMAIN}"
|
|
StreamTicket__PrivateKeyPath: "/keys/stream_private.pem"
|
|
Authentication__MetadataAddress: "https://${AUTH_DOMAIN}/.well-known/openid-configuration"
|
|
Authentication__IssuerAddress: "https://${AUTH_DOMAIN}"
|
|
Authentication__ClientId: "${AUTH_CLIENT_ID}"
|
|
Authentication__AudienceAddress: "${AUTH_CLIENT_ID}"
|
|
Authentication__AuthorizationEndpoint: "https://${AUTH_DOMAIN}/oauth/v2/authorize?prompt=login"
|
|
Authentication__TokenEndpoint: "https://${AUTH_DOMAIN}/oauth/v2/token"
|
|
Authentication__ScalarRedirectUri: "https://${API_DOMAIN}/scalar/"
|
|
Authentication__Scopes__0: "openid"
|
|
Authentication__Scopes__1: "profile"
|
|
Authentication__Scopes__2: "email"
|
|
Authentication__RequireHttpsMetadata: "true"
|
|
YtDlp__AdditionalArguments: "--cookies /secrets/youtube_cookies.txt --extractor-retries 1 --socket-timeout 15"
|
|
volumes:
|
|
- ./keys:/keys:ro
|
|
- ./secrets/youtube_cookies.txt:/secrets/youtube_cookies.txt
|
|
restart: unless-stopped
|
|
|
|
worker:
|
|
build:
|
|
context: ../backend
|
|
dockerfile: Musify.Worker.Dockerfile
|
|
container_name: musify-worker
|
|
depends_on: *backend-depends
|
|
environment:
|
|
<<: *backend-env
|
|
DOTNET_ENVIRONMENT: Production
|
|
AudioTranscoder__Routes__WorkingDirectory: "/tmp/musify"
|
|
Workers__Routes__TemporaryFilesDirectory: "/tmp/musify"
|
|
YtDlp__AdditionalArguments: "--cookies /secrets/youtube_cookies.txt --extractor-retries 1 --socket-timeout 15 -N 4"
|
|
volumes:
|
|
- ./secrets/youtube_cookies.txt:/secrets/youtube_cookies.txt
|
|
restart: unless-stopped
|
|
|
|
gateway:
|
|
build:
|
|
context: ../backend
|
|
dockerfile: Musify.StreamingGateway.Dockerfile
|
|
container_name: musify-gateway
|
|
depends_on:
|
|
seaweedfs:
|
|
condition: service_healthy
|
|
environment:
|
|
ASPNETCORE_ENVIRONMENT: Production
|
|
ASPNETCORE_URLS: http://+:8081
|
|
StreamTicket__PublicKeyPath: "/keys/stream_public.pem"
|
|
ReverseProxy__Clusters__seaweed-filer__Destinations__filer__Address: "http://seaweedfs:8888/"
|
|
volumes:
|
|
- ./keys:/keys:ro
|
|
restart: unless-stopped
|
|
|
|
frontend:
|
|
build:
|
|
context: ../frontend
|
|
dockerfile: Dockerfile
|
|
container_name: musify-frontend
|
|
depends_on:
|
|
- api
|
|
environment:
|
|
NODE_ENV: production
|
|
ORIGIN: "https://${WEB_DOMAIN}"
|
|
ZITADEL_ISSUER: "https://${AUTH_DOMAIN}"
|
|
ZITADEL_CLIENT_ID: "${WEB_CLIENT_ID}"
|
|
ZITADEL_CLIENT_SECRET: "${WEB_CLIENT_SECRET}"
|
|
AUTH_REDIRECT_URI: "https://${WEB_DOMAIN}/auth/callback"
|
|
AUTH_POST_LOGOUT_URI: "https://${WEB_DOMAIN}/"
|
|
SESSION_SECRET: "${SESSION_SECRET}"
|
|
API_BASE_URL: "http://api:5111"
|
|
restart: unless-stopped
|
|
|
|
nginx:
|
|
image: nginx:1.27-alpine
|
|
container_name: musify-nginx
|
|
depends_on:
|
|
- api
|
|
- gateway
|
|
- zitadel
|
|
- frontend
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
volumes:
|
|
- ./nginx/conf.d:/etc/nginx/conf.d:ro
|
|
- ./nginx/certs:/etc/nginx/certs:ro
|
|
restart: unless-stopped
|
|
|
|
volumes:
|
|
postgres_data:
|
|
rabbitmq_data:
|
|
seaweedfs_data:
|