Single docker-compose stack for local development (Postgres, Zitadel, RabbitMQ, SeaweedFS + bucket init, Jaeger) plus an optional overlay to run the .NET apps in Docker. up.ps1 orchestrates the full bootstrap: stack up, stream-ticket keys, EF Core migrations and Zitadel provisioning. - Zitadel is provisioned automatically (service-account PAT -> project + OIDC app with JWT access token); the generated ClientId is wired into Musify.Api user-secrets and .env. No manual console steps. - ZITADEL_EXTERNAL_DOMAIN=host.docker.internal so token validation works both with local apps and full-docker. - App Dockerfiles copy AppSettings*.json to lowercase so config loads on case-sensitive Linux; Worker image ships ffmpeg. - Scripts split by responsibility (common/stream-keys/migrate/provision); apps overlay de-duped via YAML anchors. - Remove the superseded standalone seaweedfs compose; point docs/development at the unified stack. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
147 lines
5.0 KiB
YAML
147 lines
5.0 KiB
YAML
name: musify-dev
|
|
|
|
# Infrastructure stack for Musify development: everything the backend needs
|
|
# (Postgres, Zitadel, RabbitMQ, SeaweedFS + bucket, Jaeger/OTLP). The .NET apps
|
|
# run from the IDE and connect over the published ports. Config in deploy/.env
|
|
#
|
|
# docker compose -f deploy/docker-compose.yml up -d
|
|
|
|
services:
|
|
postgres:
|
|
image: postgres:17-alpine
|
|
container_name: musify-postgres
|
|
environment:
|
|
POSTGRES_USER: ${POSTGRES_USER}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
POSTGRES_DB: ${POSTGRES_DB}
|
|
ports:
|
|
- "${POSTGRES_HOST_PORT}:5432"
|
|
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:
|
|
image: ghcr.io/zitadel/zitadel:latest
|
|
container_name: musify-zitadel
|
|
command: 'start-from-init --masterkeyFromEnv --tlsMode disabled'
|
|
environment:
|
|
ZITADEL_MASTERKEY: ${ZITADEL_MASTERKEY}
|
|
ZITADEL_EXTERNALSECURE: "false"
|
|
ZITADEL_EXTERNALDOMAIN: ${ZITADEL_EXTERNAL_DOMAIN}
|
|
ZITADEL_EXTERNALPORT: ${ZITADEL_EXTERNAL_PORT}
|
|
ZITADEL_TLS_ENABLED: "false"
|
|
# Reuses the same Postgres: the admin user creates the 'zitadel' database
|
|
# and the 'zitadel' user on first start.
|
|
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
|
|
# Initial console admin. Login: <user>@zitadel.<domain>
|
|
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: ${ZITADEL_ADMIN_USERNAME}
|
|
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: ${ZITADEL_ADMIN_PASSWORD}
|
|
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED: "false"
|
|
# Service account + PAT for automatic provisioning. Zitadel writes the PAT
|
|
# to PATPATH on init; provision.ps1 uses it to create the project and app.
|
|
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:
|
|
postgres:
|
|
condition: service_healthy
|
|
ports:
|
|
- "${ZITADEL_EXTERNAL_PORT}:8080"
|
|
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}
|
|
ports:
|
|
- "5672:5672" # AMQP -> MassTransit (API + Worker)
|
|
- "15672:15672" # management UI
|
|
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
|
|
# All-in-one dev server: master + volume + filer + S3 gateway.
|
|
command: >
|
|
server
|
|
-ip.bind=0.0.0.0
|
|
-dir=/data
|
|
-master.volumeSizeLimitMB=1024
|
|
-volume.max=0
|
|
-filer
|
|
-s3
|
|
-s3.config=/etc/seaweedfs/s3.conf
|
|
ports:
|
|
- "9333:9333" # master (admin/UI)
|
|
- "8888:8888" # filer HTTP -> StreamingGateway
|
|
- "8333:8333" # S3 API -> WebApi/Worker (InfrastructureStorage:Address)
|
|
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
|
|
|
|
# One-shot: creates the app bucket in SeaweedFS (it is not auto-created).
|
|
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"
|
|
|
|
# OTLP collector + trace UI (MassTransit/EF/HTTP spans). OtlpEndpoint = :4317
|
|
jaeger:
|
|
image: jaegertracing/all-in-one:latest
|
|
container_name: musify-jaeger
|
|
environment:
|
|
COLLECTOR_OTLP_ENABLED: "true"
|
|
ports:
|
|
- "16686:16686" # UI
|
|
- "4317:4317" # OTLP gRPC
|
|
- "4318:4318" # OTLP HTTP
|
|
restart: unless-stopped
|
|
|
|
volumes:
|
|
postgres_data:
|
|
rabbitmq_data:
|
|
seaweedfs_data:
|