name: looksee x-logging: &default-logging driver: json-file options: max-size: "10m" max-file: "5" x-healthcheck: &healthcheck-defaults interval: 15s timeout: 5s retries: 5 start_period: 30s x-service: &service-defaults restart: unless-stopped logging: *default-logging init: true x-env-file: &env-file - path: .env required: false services: storage: <<: *service-defaults image: rustfs/rustfs:1.0.0-rc.5 command: ["/data"] environment: RUSTFS_ACCESS_KEY: looksee RUSTFS_SECRET_KEY: ${STORAGE_PASSWORD:?set STORAGE_PASSWORD in .env} RUSTFS_CONSOLE_ENABLE: "false" ports: - "127.0.0.1:${STORAGE_PORT:-9000}:9000" volumes: - storage_data:/data healthcheck: <<: *healthcheck-defaults test: ["CMD", "curl", "--fail", "--silent", "http://127.0.0.1:9000/health"] deploy: resources: limits: cpus: "2.0" memory: 1g storage-init: image: ${REGISTRY:-looksee}/api:${TAG:-latest} build: context: . dockerfile: apps/api/Dockerfile restart: "no" logging: *default-logging command: ["python", "-m", "api.entrypoints.storage_init"] environment: S3_ENDPOINT_URL: http://storage:9000 S3_ACCESS_KEY_ID: looksee S3_SECRET_ACCESS_KEY: ${STORAGE_PASSWORD:?set STORAGE_PASSWORD in .env} S3_BUCKET: ${S3_BUCKET:-looksee} depends_on: storage: condition: service_healthy redis: <<: *service-defaults image: valkey/valkey:9-alpine command: valkey-server --save 60 1 --appendonly yes --appendfsync everysec --maxmemory ${REDIS_MAXMEMORY:-512mb} --maxmemory-policy noeviction --loglevel warning ports: - "127.0.0.1:${REDIS_PORT:-6379}:6379" volumes: - redis_data:/data healthcheck: <<: *healthcheck-defaults test: ["CMD", "valkey-cli", "ping"] deploy: resources: limits: cpus: "1.0" memory: 768m postgres: <<: *service-defaults image: postgres:18-alpine environment: POSTGRES_USER: ${POSTGRES_USER:-looksee} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env} POSTGRES_DB: ${POSTGRES_DB:-looksee} ports: - "127.0.0.1:${POSTGRES_PORT:-5432}:5432" volumes: - postgres_data:/var/lib/postgresql healthcheck: <<: *healthcheck-defaults start_period: 40s test: - CMD-SHELL - pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" -h 127.0.0.1 deploy: resources: limits: cpus: "2.0" memory: 2g mediamtx: <<: *service-defaults # The -ffmpeg variant ships ffmpeg for looping file sources via runOnDemand. image: bluenviron/mediamtx:1.21.2-ffmpeg ports: - "${MTX_RTSP_PORT:-8554}:8554" - "${MTX_WEBRTC_PORT:-8889}:8889" - "${MTX_WEBRTC_ICE_PORT:-8189}:8189/udp" - "127.0.0.1:9997:9997" volumes: - ./docker/mediamtx/mediamtx.yml:/mediamtx.yml:ro - media-cache:/media:ro environment: MTX_LOGLEVEL: ${MTX_LOGLEVEL:-info} MTX_AUTHHTTPADDRESS: ${MTX_AUTHHTTPADDRESS:-http://api:8000/internal/media/auth} MTX_WEBRTCADDITIONALHOSTS: ${WEBRTC_HOST_IP:?set WEBRTC_HOST_IP in .env (127.0.0.1 locally, the box LAN IP on-prem)} deploy: resources: limits: cpus: "2.0" memory: 1g media-cache-init: image: ${REGISTRY:-looksee}/api:${TAG:-latest} build: context: . dockerfile: apps/api/Dockerfile restart: "no" logging: *default-logging user: "0:0" entrypoint: ["sh", "-c", "rm -rf /cache/cdrom /cache/floppy /cache/usb && chown -R api:api /cache"] volumes: - media-cache:/cache api-migrate: image: ${REGISTRY:-looksee}/api:${TAG:-latest} build: context: . dockerfile: apps/api/Dockerfile restart: "no" logging: *default-logging command: ["alembic", "-c", "apps/api/alembic.ini", "upgrade", "head"] environment: DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-looksee}:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-looksee} depends_on: postgres: condition: service_healthy api: <<: *service-defaults image: ${REGISTRY:-looksee}/api:${TAG:-latest} build: context: . dockerfile: apps/api/Dockerfile env_file: *env-file environment: DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-looksee}:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-looksee} REDIS_URL: redis://redis:6379/0 MEDIAMTX_API_URL: http://mediamtx:9997 MTX_MEDIA_USER: ${MTX_MEDIA_USER:-media} MTX_MEDIA_PASSWORD: ${MTX_MEDIA_PASSWORD:?set MTX_MEDIA_PASSWORD in .env} MODELS_DIR: /app/models SNAPSHOTS_DIR: /data/snapshots SECRET_KEY_FILE: /data/keys/secret.key MEDIA_CACHE_DIR: /var/lib/looksee/media-cache S3_ENDPOINT_URL: ${S3_ENDPOINT_URL:-http://storage:9000} S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:-looksee} S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:-${STORAGE_PASSWORD:?set STORAGE_PASSWORD in .env}} S3_BUCKET: ${S3_BUCKET:-looksee} volumes: - api_snapshots:/data/snapshots - api_keys:/data/keys - media-cache:/var/lib/looksee/media-cache - ./models:/app/models:ro ports: - "${API_PORT:-8000}:8000" healthcheck: <<: *healthcheck-defaults test: - CMD - python - -c - import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).status == 200 else 1) depends_on: postgres: condition: service_healthy restart: true redis: condition: service_healthy restart: true api-migrate: condition: service_completed_successfully media-cache-init: condition: service_completed_successfully storage-init: condition: service_completed_successfully deploy: resources: limits: cpus: "2.0" memory: 1g inference: <<: *service-defaults image: ${REGISTRY:-looksee}/inference:${TAG:-latest} build: context: . dockerfile: apps/inference/Dockerfile env_file: *env-file environment: REDIS_URL: redis://redis:6379/0 MEDIAMTX_RTSP_URL: rtsp://mediamtx:8554 MTX_MEDIA_USER: ${MTX_MEDIA_USER:-media} MTX_MEDIA_PASSWORD: ${MTX_MEDIA_PASSWORD:?set MTX_MEDIA_PASSWORD in .env} MODELS_DIR: /app/models healthcheck: <<: *healthcheck-defaults test: - CMD - python - -c - import os,redis; redis.from_url(os.environ['REDIS_URL'], socket_timeout=4).ping() depends_on: redis: condition: service_healthy restart: true mediamtx: condition: service_started volumes: - ./models:/app/models:ro deploy: resources: limits: cpus: ${INFERENCE_CPUS:-4.0} memory: ${INFERENCE_MEMORY:-4g} studio: <<: *service-defaults image: ${REGISTRY:-looksee}/studio:${TAG:-latest} build: context: . dockerfile: apps/studio/Dockerfile environment: RUNTIME_API_URL: ${RUNTIME_API_URL:-http://${WEBRTC_HOST_IP}:${API_PORT:-8000}} RUNTIME_WS_URL: ${RUNTIME_WS_URL:-ws://${WEBRTC_HOST_IP}:${API_PORT:-8000}} RUNTIME_MEDIAMTX_WEBRTC_URL: ${RUNTIME_MEDIAMTX_WEBRTC_URL:-http://${WEBRTC_HOST_IP}:${MTX_WEBRTC_PORT:-8889}} RUNTIME_DOCS_URL: https://github.com/okoflow/looksee-docs RUNTIME_GITHUB_URL: ${RUNTIME_GITHUB_URL:-https://github.com/okoflow/looksee} SERVER_API_URL: http://api:8000 ports: - "${WEB_PORT:-3000}:3000" healthcheck: <<: *healthcheck-defaults test: - CMD - node - -e - fetch('http://127.0.0.1:3000/api/health').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1)) depends_on: api: condition: service_healthy restart: true deploy: resources: limits: cpus: "1.0" memory: 512m volumes: storage_data: redis_data: postgres_data: api_snapshots: api_keys: media-cache: