services: postgresql: image: postgres:18 env_file: - env.d/development/postgresql ports: - "16432:5432" redis: image: redis:5 mailcatcher: image: sj26/mailcatcher:latest ports: - "1081:1080" rustfs: user: ${DOCKER_USER:-1000} image: rustfs/rustfs:latest environment: - RUSTFS_ACCESS_KEY=dictaphone - RUSTFS_SECRET_KEY=password - RUSTFS_SERVER_DOMAINS=rustfs - RUSTFS_CORS_ALLOWED_ORIGINS=http://localhost:3000 ports: - '9000:9000' - '9001:9001' healthcheck: test: [ "CMD-SHELL", "curl -fsS http://localhost:9000/health/ready >/dev/null" ] interval: 1s timeout: 20s retries: 300 command: ["/data"] networks: default: aliases: - dictaphone-media-storage.rustfs - dictaphone-media-alpha.rustfs - dictaphone-media-beta.rustfs volumes: - ./data/media:/data createbuckets: image: rustfs/rc:latest depends_on: rustfs: condition: service_healthy restart: true entrypoint: > sh -c " rc alias set dictaphone http://rustfs:9000 dictaphone password && \ rc mb --ignore-existing dictaphone/dictaphone-media-storage && \ rc mb --ignore-existing dictaphone/dictaphone-media-alpha && \ rc mb --ignore-existing dictaphone/dictaphone-media-beta && \ rc admin user add dictaphone dictaphone-default password-default || true; \ rc admin user add dictaphone dictaphone-alpha password-alpha || true; \ rc admin user add dictaphone dictaphone-beta password-beta || true; \ printf '%s' '{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":[\"s3:*\"],\"Effect\":\"Allow\",\"Resource\":[\"arn:aws:s3:::dictaphone-media-storage\",\"arn:aws:s3:::dictaphone-media-storage/*\"]}]}' >/tmp/default-policy.json && \ printf '%s' '{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":[\"s3:*\"],\"Effect\":\"Allow\",\"Resource\":[\"arn:aws:s3:::dictaphone-media-alpha\",\"arn:aws:s3:::dictaphone-media-alpha/*\"]}]}' >/tmp/alpha-policy.json && \ printf '%s' '{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":[\"s3:*\"],\"Effect\":\"Allow\",\"Resource\":[\"arn:aws:s3:::dictaphone-media-beta\",\"arn:aws:s3:::dictaphone-media-beta/*\"]}]}' >/tmp/beta-policy.json && \ rc admin policy create dictaphone default-bucket-policy /tmp/default-policy.json || true; \ rc admin policy create dictaphone alpha-bucket-policy /tmp/alpha-policy.json || true; \ rc admin policy create dictaphone beta-bucket-policy /tmp/beta-policy.json || true; \ rc admin policy attach dictaphone default-bucket-policy --user dictaphone-default || true; \ rc admin policy attach dictaphone alpha-bucket-policy --user dictaphone-alpha || true; \ rc admin policy attach dictaphone beta-bucket-policy --user dictaphone-beta || true; \ exit 0;" app-dev: build: context: . target: backend-development args: DOCKER_USER: ${DOCKER_USER:-1000} user: ${DOCKER_USER:-1000} image: dictaphone:backend-development environment: - PYLINTHOME=/app/.pylint.d - DJANGO_CONFIGURATION=Development - AUDIO_JOB_TIMEOUT_SECONDS=${AUDIO_JOB_TIMEOUT_SECONDS:-300} - AUDIO_EXTRACTOR_TRANSCODING_TOKEN=${AUDIO_EXTRACTOR_TRANSCODING_TOKEN:-local-development-transcoding-token} - AUDIO_EXTRACTOR_VALIDATION_TOKEN=${AUDIO_EXTRACTOR_VALIDATION_TOKEN:-local-development-validation-token} env_file: - env.d/development/common - env.d/development/postgresql ports: - "8071:8000" volumes: - ./src/backend:/app - ./data/static:/data/static - /app/.venv depends_on: - postgresql - mailcatcher - redis - createbuckets extra_hosts: - "127.0.0.1.nip.io:host-gateway" networks: - resource-server - default celery-dev: user: ${DOCKER_USER:-1000} image: dictaphone:backend-development command: ["celery", "-A", "dictaphone.celery_app", "worker", "-l", "DEBUG", "--concurrency=1", "-Q", "dictaphone-backend"] environment: - DJANGO_CONFIGURATION=Development - AUDIO_JOB_TIMEOUT_SECONDS=${AUDIO_JOB_TIMEOUT_SECONDS:-300} - AUDIO_EXTRACTOR_TRANSCODING_TOKEN=${AUDIO_EXTRACTOR_TRANSCODING_TOKEN:-local-development-transcoding-token} - AUDIO_EXTRACTOR_VALIDATION_TOKEN=${AUDIO_EXTRACTOR_VALIDATION_TOKEN:-local-development-validation-token} env_file: - env.d/development/common - env.d/development/postgresql volumes: - ./src/backend:/app - ./data/static:/data/static - /app/.venv depends_on: - app-dev audio-extractor-dev: build: context: . dockerfile: src/audio-extractor/Dockerfile target: development args: DOCKER_USER: ${DOCKER_USER:-1000} user: ${DOCKER_USER:-1000} image: dictaphone:audio-extractor-development depends_on: rustfs: condition: service_healthy s3-gateway: condition: service_healthy createbuckets: condition: service_completed_successfully working_dir: /app environment: PYLINTHOME: /tmp/pylint S3_GATEWAY_URL: http://s3-gateway:8080/proxy S3_ENDPOINT_URL: http://rustfs:9000 volumes: - ./src/audio-extractor:/app - /app/.venv audio-extractor-transcoding-dev: build: context: . dockerfile: src/audio-extractor/Dockerfile target: audio-extractor-production args: DOCKER_USER: ${DOCKER_USER:-1000} user: ${DOCKER_USER:-1000} image: dictaphone:audio-extractor environment: AUDIO_EXTRACTOR_API_URL: http://app-dev:8000/api/internal/v1.0 AUDIO_EXTRACTOR_TOKEN: ${AUDIO_EXTRACTOR_TRANSCODING_TOKEN:-local-development-transcoding-token} S3_GATEWAY_URL: ${S3_GATEWAY_URL-http://s3-gateway:8080/proxy} AUDIO_EXTRACTOR_MODE: transcoding AUDIO_EXTRACTOR_JOB_TIMEOUT_SECONDS: ${AUDIO_JOB_TIMEOUT_SECONDS:-300} AUDIO_EXTRACTOR_MAX_RUNTIME_SECONDS: ${AUDIO_EXTRACTOR_MAX_RUNTIME_SECONDS:-900} AUDIO_EXTRACTOR_PROCESS_TIMEOUT_SECONDS: ${AUDIO_EXTRACTOR_PROCESS_TIMEOUT_SECONDS:-1200} AUDIO_EXTRACTOR_SHUTDOWN_GRACE_SECONDS: ${AUDIO_EXTRACTOR_SHUTDOWN_GRACE_SECONDS:-30} AUDIO_EXTRACTOR_POLL_INTERVAL_SECONDS: ${AUDIO_EXTRACTOR_POLL_INTERVAL_SECONDS:-5} stop_grace_period: 45s healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/livez', timeout=2)"] interval: 10s timeout: 3s retries: 3 start_period: 10s depends_on: app-dev: condition: service_started s3-gateway: condition: service_healthy tmpfs: - /work:size=1g restart: unless-stopped audio-extractor-validation-dev: build: context: . dockerfile: src/audio-extractor/Dockerfile target: audio-extractor-production args: DOCKER_USER: ${DOCKER_USER:-1000} user: ${DOCKER_USER:-1000} image: dictaphone:audio-extractor environment: AUDIO_EXTRACTOR_API_URL: http://app-dev:8000/api/internal/v1.0 AUDIO_EXTRACTOR_TOKEN: ${AUDIO_EXTRACTOR_VALIDATION_TOKEN:-local-development-validation-token} S3_GATEWAY_URL: ${S3_GATEWAY_URL-http://s3-gateway:8080/proxy} AUDIO_EXTRACTOR_MODE: validation AUDIO_EXTRACTOR_JOB_TIMEOUT_SECONDS: ${AUDIO_JOB_TIMEOUT_SECONDS:-300} AUDIO_EXTRACTOR_MAX_RUNTIME_SECONDS: ${AUDIO_EXTRACTOR_MAX_RUNTIME_SECONDS:-900} AUDIO_EXTRACTOR_PROCESS_TIMEOUT_SECONDS: ${AUDIO_EXTRACTOR_PROCESS_TIMEOUT_SECONDS:-1200} AUDIO_EXTRACTOR_SHUTDOWN_GRACE_SECONDS: ${AUDIO_EXTRACTOR_SHUTDOWN_GRACE_SECONDS:-30} AUDIO_EXTRACTOR_POLL_INTERVAL_SECONDS: ${AUDIO_EXTRACTOR_POLL_INTERVAL_SECONDS:-5} stop_grace_period: 45s healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/livez', timeout=2)"] interval: 10s timeout: 3s retries: 3 start_period: 10s depends_on: app-dev: condition: service_started s3-gateway: condition: service_healthy tmpfs: - /work:size=1g restart: unless-stopped audio-extractor-transcoding: build: context: . dockerfile: src/audio-extractor/Dockerfile target: audio-extractor-production args: DOCKER_USER: ${DOCKER_USER:-1000} user: ${DOCKER_USER:-1000} image: dictaphone:audio-extractor-production environment: AUDIO_EXTRACTOR_API_URL: http://app:8000/api/internal/v1.0 AUDIO_EXTRACTOR_TOKEN: ${AUDIO_EXTRACTOR_TRANSCODING_TOKEN:-local-development-transcoding-token} S3_GATEWAY_URL: ${S3_GATEWAY_URL-http://s3-gateway:8080/proxy} AUDIO_EXTRACTOR_MODE: transcoding AUDIO_EXTRACTOR_JOB_TIMEOUT_SECONDS: ${AUDIO_JOB_TIMEOUT_SECONDS:-300} AUDIO_EXTRACTOR_MAX_RUNTIME_SECONDS: ${AUDIO_EXTRACTOR_MAX_RUNTIME_SECONDS:-900} AUDIO_EXTRACTOR_PROCESS_TIMEOUT_SECONDS: ${AUDIO_EXTRACTOR_PROCESS_TIMEOUT_SECONDS:-1200} AUDIO_EXTRACTOR_SHUTDOWN_GRACE_SECONDS: ${AUDIO_EXTRACTOR_SHUTDOWN_GRACE_SECONDS:-30} AUDIO_EXTRACTOR_POLL_INTERVAL_SECONDS: ${AUDIO_EXTRACTOR_POLL_INTERVAL_SECONDS:-5} stop_grace_period: 45s healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/livez', timeout=2)"] interval: 10s timeout: 3s retries: 3 start_period: 10s depends_on: app: condition: service_started s3-gateway: condition: service_healthy tmpfs: - /work:size=1g restart: unless-stopped audio-extractor-validation: build: context: . dockerfile: src/audio-extractor/Dockerfile target: audio-extractor-production args: DOCKER_USER: ${DOCKER_USER:-1000} user: ${DOCKER_USER:-1000} image: dictaphone:audio-extractor-production environment: AUDIO_EXTRACTOR_API_URL: http://app:8000/api/internal/v1.0 AUDIO_EXTRACTOR_TOKEN: ${AUDIO_EXTRACTOR_VALIDATION_TOKEN:-local-development-validation-token} S3_GATEWAY_URL: ${S3_GATEWAY_URL-http://s3-gateway:8080/proxy} AUDIO_EXTRACTOR_MODE: validation AUDIO_EXTRACTOR_JOB_TIMEOUT_SECONDS: ${AUDIO_JOB_TIMEOUT_SECONDS:-300} AUDIO_EXTRACTOR_MAX_RUNTIME_SECONDS: ${AUDIO_EXTRACTOR_MAX_RUNTIME_SECONDS:-900} AUDIO_EXTRACTOR_PROCESS_TIMEOUT_SECONDS: ${AUDIO_EXTRACTOR_PROCESS_TIMEOUT_SECONDS:-1200} AUDIO_EXTRACTOR_SHUTDOWN_GRACE_SECONDS: ${AUDIO_EXTRACTOR_SHUTDOWN_GRACE_SECONDS:-30} AUDIO_EXTRACTOR_POLL_INTERVAL_SECONDS: ${AUDIO_EXTRACTOR_POLL_INTERVAL_SECONDS:-5} stop_grace_period: 45s healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/livez', timeout=2)"] interval: 10s timeout: 3s retries: 3 start_period: 10s depends_on: app: condition: service_started s3-gateway: condition: service_healthy tmpfs: - /work:size=1g restart: unless-stopped s3-gateway: build: context: . dockerfile: src/audio-extractor/Dockerfile target: audio-extractor-production image: dictaphone:audio-extractor command: ["python", "-m", "audio_extractor.s3_gateway"] env_file: - env.d/development/s3-gateway environment: S3_GATEWAY_MAX_UPLOAD_BYTES: ${S3_GATEWAY_MAX_UPLOAD_BYTES:-1073741824} S3_GATEWAY_MAX_CONCURRENT_REQUESTS: ${S3_GATEWAY_MAX_CONCURRENT_REQUESTS:-20} S3_GATEWAY_TIMEOUT_SECONDS: ${S3_GATEWAY_TIMEOUT_SECONDS:-3600} depends_on: rustfs: condition: service_healthy read_only: true cap_drop: [ALL] security_opt: [no-new-privileges:true] stop_grace_period: 45s healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/healthz', timeout=2)"] interval: 10s timeout: 3s retries: 3 restart: unless-stopped app: build: context: . target: backend-production args: DOCKER_USER: ${DOCKER_USER:-1000} user: ${DOCKER_USER:-1000} image: dictaphone:backend-production environment: - DJANGO_CONFIGURATION=Demo - AUDIO_JOB_TIMEOUT_SECONDS=${AUDIO_JOB_TIMEOUT_SECONDS:-300} - AUDIO_EXTRACTOR_TRANSCODING_TOKEN=${AUDIO_EXTRACTOR_TRANSCODING_TOKEN:-local-development-transcoding-token} - AUDIO_EXTRACTOR_VALIDATION_TOKEN=${AUDIO_EXTRACTOR_VALIDATION_TOKEN:-local-development-validation-token} env_file: - env.d/development/common - env.d/development/postgresql depends_on: - postgresql - redis - rustfs celery: user: ${DOCKER_USER:-1000} image: dictaphone:backend-production command: ["celery", "-A", "dictaphone.celery_app", "worker", "-l", "INFO", "--concurrency=1", "-Q", "dictaphone-backend"] environment: - DJANGO_CONFIGURATION=Demo - AUDIO_JOB_TIMEOUT_SECONDS=${AUDIO_JOB_TIMEOUT_SECONDS:-300} - AUDIO_EXTRACTOR_TRANSCODING_TOKEN=${AUDIO_EXTRACTOR_TRANSCODING_TOKEN:-local-development-transcoding-token} - AUDIO_EXTRACTOR_VALIDATION_TOKEN=${AUDIO_EXTRACTOR_VALIDATION_TOKEN:-local-development-validation-token} env_file: - env.d/development/common - env.d/development/postgresql depends_on: - app nginx: image: nginx:1.25 ports: - "8083:8083" volumes: - ./docker/files/etc/nginx/conf.d:/etc/nginx/conf.d:ro depends_on: - keycloak - app-dev networks: - resource-server - default frontend: user: "${DOCKER_USER:-1000}" build: context: . dockerfile: ./src/frontend/Dockerfile target: frontend-production args: VITE_API_BASE_URL: "http://localhost:8071" VITE_APP_TITLE: "Assistant Transcripts" image: dictaphone:frontend-development ports: - "3000:8080" dockerize: image: jwilder/dockerize platform: linux/x86_64 crowdin: image: crowdin/cli:4.0.0 volumes: - ".:/app" env_file: - env.d/development/crowdin user: "${DOCKER_USER:-1000}" working_dir: /app node: image: node:18 user: "${DOCKER_USER:-1000}" environment: HOME: /tmp volumes: - ".:/app" kc_postgresql: image: postgres:14.3 ports: - "5433:5432" env_file: - env.d/development/kc_postgresql keycloak: image: quay.io/keycloak/keycloak:20.0.1 volumes: - ./docker/auth/realm.json:/opt/keycloak/data/import/realm.json command: - start-dev - --features=preview - --import-realm - --proxy=edge - --hostname-url=http://localhost:8083 - --hostname-admin-url=http://localhost:8083/ - --hostname-strict=false - --hostname-strict-https=false environment: KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin KC_DB: postgres KC_DB_URL_HOST: kc_postgresql KC_DB_URL_DATABASE: keycloak KC_DB_PASSWORD: pass KC_DB_USERNAME: dictaphone KC_DB_SCHEMA: public PROXY_ADDRESS_FORWARDING: 'true' ports: - "8080:8080" depends_on: - kc_postgresql networks: default: resource-server: