Files
Musify/deploy/docker-compose.prod.yml
T
Admin 4b06226dd7 refactor(deploy): rename the frontend service to web-player
Matches the folder rename: compose service frontend -> web-player,
container musify-frontend -> musify-web-player, and the nginx upstream
now points at http://web-player:3000.

Deploying this needs --remove-orphans, or the old musify-frontend
container is left behind.
2026-07-20 07:37:31 +02:00

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
web-player:
build:
context: ../web-player
dockerfile: Dockerfile
container_name: musify-web-player
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
- web-player
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: