name: tasteboard services: web: build: . image: tasteboard-app:local restart: unless-stopped ports: ["${BIND_ADDRESS:-127.0.0.1}:${APP_PORT:-3000}:3000"] environment: &app-environment DATABASE_URL: postgresql://tasteboard:${POSTGRES_PASSWORD:-tasteboard}@database:5432/tasteboard ORIGIN: ${ORIGIN:-http://localhost:3000} DATA_DIR: /data TASTEBOARD_CODEX_HOME: /codex MEDIA_WORKER_URL: http://media:8100 MAX_FILE_BYTES: ${MAX_FILE_BYTES:-104857600} BODY_SIZE_LIMIT: ${BODY_SIZE_LIMIT:-110M} APP_MODE: ${APP_MODE:-owner} TASTE_SOURCE_URL: ${TASTE_SOURCE_URL:-} TASTE_SOURCE_KEY: ${TASTE_SOURCE_KEY:-} volumes: ["data:/data", "codex:/codex"] depends_on: database: {condition: service_healthy} healthcheck: 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))"] interval: 30s timeout: 5s retries: 5 worker: image: tasteboard-app:local restart: unless-stopped command: ["node", "--import", "tsx", "src/worker.ts"] environment: *app-environment volumes: ["data:/data", "codex:/codex"] depends_on: web: {condition: service_healthy} media: {condition: service_healthy} mem_limit: 2g cpus: 2 media: build: ./media restart: unless-stopped environment: DATA_DIR: /data MODEL_DIR: /models ML_THREADS: ${ML_THREADS:-2} volumes: ["data:/data", "models:/models"] mem_limit: 3g cpus: 2 healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8100/health')"] interval: 15s timeout: 5s start_period: 180s retries: 20 database: image: pgvector/pgvector:pg17 restart: unless-stopped environment: POSTGRES_USER: tasteboard POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-tasteboard} POSTGRES_DB: tasteboard volumes: ["database:/var/lib/postgresql/data"] healthcheck: test: ["CMD-SHELL", "pg_isready -U tasteboard"] interval: 5s timeout: 5s retries: 20 volumes: database: data: codex: models: