3.5 KiB · text History 07a12d4
version: "2"
services:
  postgres:
    image: postgres:17
    container_name: dms_postgres
    ports:
      - 5433:5432
    environment:
      POSTGRES_DB: dms
      POSTGRES_USER: dms_user
      POSTGRES_PASSWORD: dms_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U dms_user -d dms"]
      interval: 5s
      timeout: 3s
      retries: 10
  rabbitmq:
    image: rabbitmq:3-management-alpine
    container_name: rabbitmq_oxxy
    ports:
      - 5674:5672
      - 15674:15672
    environment:
      RABBITMQ_DEFAULT_USER: dms_user
      RABBITMQ_DEFAULT_PASS: dms_password
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
      interval: 10s
      timeout: 5s
      retries: 10

  # ---------- DMS app + workers ----------
  # Build once from the local Dockerfile, then re-use that image for every
  # downstream service via the YAML anchor below. Run `docker compose build`
  # to rebuild after a code change.
  webservice:
    build:
      context: .
    image: dms:local
    container_name: dms_webservice
    depends_on:
      postgres: { condition: service_healthy }
      rabbitmq: { condition: service_healthy }
    environment:
      DATABASE_URL: postgres://dms_user:dms_password@postgres:5432/dms
      AMQP_URL: amqp://dms_user:dms_password@rabbitmq:5672/%2f
      BIND_ADDRESS: 0.0.0.0:3007
      STORAGE_ROOT: /data/storage
      # 5 GiB on the wire, 8 GiB of decompressed zip contents. NOTE: an upload
      # is buffered in memory end to end, so a request this size costs that
      # much RSS while it runs — size the container accordingly, and keep the
      # reverse proxy's own body limit at least as large or it rejects first.
      MAX_UPLOAD_BYTES: "5368709120"
      MAX_ZIP_TOTAL_BYTES: "8589934592"
      AUTO_MIGRATE: "true"
      REGISTRATION_ENABLED: "false"
      # Set this to the public origin so the QR pairing URL works from a
      # phone on the same network. Leave unset for LAN — the handler falls
      # back to the request's Host header.
      # DMS_PUBLIC_URL: https://dms.example.com
    ports:
      - 3007:3007
    volumes:
      - dms_data:/data

  # All worker services share this base — only WORKER_KIND differs. The image's
  # default CMD is `dms-webservice`, so we override CMD once on the anchor.
  worker-thumbnail: &worker
    image: dms:local
    depends_on:
      webservice: { condition: service_started }
    environment: &worker-env
      DATABASE_URL: postgres://dms_user:dms_password@postgres:5432/dms
      AMQP_URL: amqp://dms_user:dms_password@rabbitmq:5672/%2f
      STORAGE_ROOT: /data/storage
      # OCRS models are bundled into the image at /opt/dms/models — only
      # set OCRS_*_MODEL here if you swap to different model files.
      WORKER_PREFETCH: "4"
      WORKER_KIND: thumbnail
    volumes:
      - dms_data:/data
    command: ["dms-queue-worker"]

  # Scale each kind independently:
  # `podman compose up -d --scale worker-ocr=3`
  # Runs before thumbnail/ocr/describe for video uploads and enqueues them
  # once it has extracted a frame. Needs the ffmpeg in the image.
  worker-poster:
    <<: *worker
    environment:
      <<: *worker-env
      WORKER_KIND: poster
  worker-ocr:
    <<: *worker
    environment:
      <<: *worker-env
      WORKER_KIND: ocr
  worker-describe:
    <<: *worker
    environment:
      <<: *worker-env
      WORKER_KIND: describe
  worker-extract-tags:
    <<: *worker
    environment:
      <<: *worker-env
      WORKER_KIND: extract_tags

volumes:
  postgres_data:
  dms_data: