services: caddy: image: caddy:2.11.4-alpine container_name: caddy volumes: - caddy_data:/data - caddy_config:/config - ./Caddyfile:/etc/caddy/Caddyfile restart: unless-stopped ports: - "8080:8080" - "8082:8082" - "8084:8084" logging: driver: local options: mode: non-blocking max-size: 10m max-file: "3" web: container_name: web image: ghcr.io/oxynote/web:dev environment: # SSR inside the container must reach the services directly; these # compose hostnames don't resolve on the host, which is why they live # here and not in the shared env template. - NUXT_CORE_API_INTERNAL_HTTP_URL=http://core:8080 - NUXT_AUTH_REALTIME_API_INTERNAL_HTTP_URL=http://auth-realtime:8081 # not started by plain `up` — `make dev` runs web on the host instead; # `make run` / `make start` enable this profile for the full stack. profiles: - web build: context: ../web dockerfile: Dockerfile.dev env_file: - ./env/web.local.env restart: unless-stopped logging: driver: local options: mode: non-blocking max-size: 10m max-file: "3" auth-realtime: container_name: auth-realtime image: ghcr.io/oxynote/auth-realtime:dev build: context: ../server/auth-realtime dockerfile: Dockerfile.dev env_file: - ./env/auth-realtime.local.env depends_on: postgres: condition: service_healthy valkey: condition: service_healthy restart: unless-stopped logging: driver: local options: mode: non-blocking max-size: 10m max-file: "3" core: container_name: core image: ghcr.io/oxynote/core:dev platform: linux/amd64 env_file: - ./env/core.local.env depends_on: postgres: condition: service_healthy meilisearch: condition: service_started volumes: # optional GitHub App private key: drop oxynote.private-key.pem into # docker/github/ and set OXYNOTE_CORE_GITHUB_APP_ID to enable the # GitHub App integration. - ./github:/etc/oxynote/core/github:ro # uploaded files, which core keeps under OXYNOTE_CORE_OBJECT_STORAGE_LOCAL_PATH # when no object store is configured. The mount is the home directory # rather than the storage path itself: core runs as a non-root uid # that owns only /home/nonroot in its image, and an empty volume # takes the ownership of the directory it is mounted over. - core_storage:/home/nonroot restart: unless-stopped logging: driver: local options: mode: non-blocking max-size: 10m max-file: "3" datagen: container_name: datagen image: ghcr.io/oxynote/datagen:dev platform: linux/amd64 environment: - DATAGEN_POSTGRES_DSN=postgresql://demouser:demopass@postgres/demodb?sslmode=disable - DATAGEN_MARIADB_DSN=demouser:demopass@tcp(mariadb:3306)/demodb?parseTime=true depends_on: postgres: condition: service_healthy mariadb: condition: service_healthy restart: unless-stopped logging: driver: local options: mode: non-blocking max-size: 10m max-file: "3" postgres: image: postgres:18.6-alpine volumes: - postgres_data:/var/lib/postgresql - ./demo/postgres/init-demo-db.sh:/docker-entrypoint-initdb.d/init-demo-db.sh environment: POSTGRES_USER: devuser POSTGRES_PASSWORD: devpass POSTGRES_DB: devdb restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U devuser -d devdb"] interval: 10s timeout: 5s retries: 5 logging: driver: local options: mode: non-blocking max-size: 10m max-file: "3" mariadb: image: mariadb:12.3.2-noble volumes: - mariadb_data:/var/lib/mysql - ./demo/mariadb/init-demo-db.sql:/docker-entrypoint-initdb.d/init-demo-db.sql environment: MARIADB_ROOT_PASSWORD: devroot MARIADB_USER: devuser MARIADB_PASSWORD: devpass MARIADB_DATABASE: devdb restart: unless-stopped healthcheck: test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] interval: 10s timeout: 5s retries: 5 logging: driver: local options: mode: non-blocking max-size: 10m max-file: "3" meilisearch: container_name: meilisearch image: getmeili/meilisearch:v1.53.1 volumes: - meilisearch_data:/meili_data environment: - MEILI_MASTER_KEY=devmasterkey - MEILI_ENV=development restart: unless-stopped logging: driver: local options: mode: non-blocking max-size: 10m max-file: "3" mailpit: container_name: mailpit image: axllent/mailpit:v1.31.0 restart: unless-stopped logging: driver: local options: mode: non-blocking max-size: 10m max-file: "3" valkey: container_name: valkey image: valkey/valkey:9.1.1-alpine volumes: - valkey_data:/data restart: unless-stopped healthcheck: test: ["CMD", "valkey-cli", "ping"] interval: 10s timeout: 5s retries: 5 logging: driver: local options: mode: non-blocking max-size: 10m max-file: "3" changedetection: container_name: changedetection image: dgtlmoon/changedetection.io:0.55.8 volumes: - changedetection_data:/datastore environment: - HIDE_REFERER=true - LOGGER_LEVEL=WARNING - PLAYWRIGHT_DRIVER_URL=ws://browser-sockpuppet-chrome:3000 depends_on: browser-sockpuppet-chrome: condition: service_started restart: unless-stopped logging: driver: local options: mode: non-blocking max-size: 10m max-file: "3" browser-sockpuppet-chrome: container_name: browser-sockpuppet-chrome hostname: browser-sockpuppet-chrome # upstream publishes no per-release tags (its version tags lag latest by # months), so latest is pinned by digest instead; dependabot bumps the # digest when a new latest is published. image: dgtlmoon/sockpuppetbrowser:latest@sha256:7116c61ef9cfce3d48a7efd9355d2fbe19f593ea3cfb52a5ded40ecbcb0a3f9d cap_add: - SYS_ADMIN # TODO: Recommended in their docker-compose example, need to double-check this. restart: unless-stopped environment: - SCREEN_WIDTH=1920 - SCREEN_HEIGHT=1024 - SCREEN_DEPTH=16 - MAX_CONCURRENT_CHROME_PROCESSES=10 - LOG_LEVEL=WARNING grafana: image: grafana/grafana:13.2.0 container_name: grafana ports: - "8083:3000" volumes: - grafana_storage:/var/lib/grafana environment: - GF_SECURITY_ADMIN_USER=admin - GF_SECURITY_ADMIN_PASSWORD=admin volumes: core_storage: mariadb_data: postgres_data: meilisearch_data: valkey_data: caddy_data: caddy_config: changedetection_data: grafana_storage: