# Local development overrides: a self-contained stack with its own Traefik, # published host ports, hot reload and the mail/browser test helpers. # Never used in production — the Makefile only layers it for `make dev*`. services: # Local Traefik. Services stay reachable both on their published ports and # on http://api.localhost / http://app.localhost via the routers in # compose.yml. Dashboard at http://localhost:8090. proxy: image: traefik:3.6 volumes: - /var/run/docker.sock:/var/run/docker.sock ports: - "80:80" - "8090:8080" command: - --providers.docker - --providers.docker.constraints=Label(`traefik.constraint-label`, `proxy`) - --providers.docker.exposedbydefault=false - --entrypoints.http.address=:80 - --entrypoints.https.address=:443 - --accesslog - --log - --log.level=DEBUG - --api - --api.insecure=true labels: - traefik.enable=true - traefik.constraint-label=proxy # Dummy https-redirect middleware that doesn't really redirect, so the # production routers' middleware reference still resolves locally. - traefik.http.middlewares.https-redirect.contenttype.autodetect=false networks: - proxy - default # Both only start under the analytics profile now — the engine's own state # is SQLite on the data volume. db: restart: "no" ports: - "5432:5432" adminer: restart: "no" ports: - "127.0.0.1:8080:8080" redis: restart: "no" # A broker and a time-series database of the stack's own, so the mqtt and # influx node types are testable without external hardware. Point a node at # broker_host `mosquitto` or url `http://influxdb:8086`. # # Neither publishes a host port on purpose: nothing outside the stack needs # them, and 1883 in particular tends to be taken by the real broker on a # machine that runs this alongside anything else. mosquitto: image: eclipse-mosquitto:2 restart: "no" # The config the image ships for exactly this: listener on 1883, anonymous # clients allowed. Its default config accepts neither. command: mosquitto -c /mosquitto-no-auth.conf influxdb: image: influxdb:2.7 restart: "no" # Dev credentials, and no volume: the container comes up initialised and a # `down -v` is all it takes to start over. environment: DOCKER_INFLUXDB_INIT_MODE: setup DOCKER_INFLUXDB_INIT_USERNAME: fluksio DOCKER_INFLUXDB_INIT_PASSWORD: fluksio-dev-password DOCKER_INFLUXDB_INIT_ORG: fluksio DOCKER_INFLUXDB_INIT_BUCKET: fluksio DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: fluksio-dev-token backend: restart: "no" ports: - "8000:8000" # Not a startup requirement — listed so the two come up with the stack, # which starts its services by name (`make dev-local`). depends_on: mosquitto: condition: service_started influxdb: condition: service_started build: context: .. dockerfile: backend/Dockerfile command: - fastapi - run - --reload - "fluksio/main.py" develop: watch: - path: ../backend action: sync target: /app/backend ignore: - ../backend/.venv - .venv - path: ../backend/pyproject.toml action: rebuild volumes: # What makes --reload above mean anything in the detached flow (`make dev`, # `make dev-local`): it starts the stack with `up -d`, which never runs the # sync above — only `docker compose watch` does. Without the mount the # container serves the source baked into the image until it is rebuilt. - ../backend/fluksio:/app/backend/fluksio # The worker speaks a protocol the engine version-matches, so mounting one # without the other leaves a reloaded engine talking to the worker baked # into the image — which is how every python node came to fail with # "the worker answered request None" after a protocol change. - ../worker/fluksio_worker:/app/worker/fluksio_worker environment: SMTP_HOST: "mailcatcher" SMTP_PORT: "1025" SMTP_TLS: "false" EMAILS_FROM_EMAIL: "info@fluksio.com" # Test-only user seeding, needed by the Playwright suite. Dev stack only. PRIVATE_API_ENABLED: "true" mailcatcher: image: schickling/mailcatcher ports: - "1080:1080" - "1025:1025" frontend: restart: "no" ports: - "5173:80" build: context: .. dockerfile: frontend/Dockerfile args: - VITE_API_URL=http://localhost:8000 - NODE_ENV=development # Reachable at http://docs.${DOMAIN}, but a container rebuild per edit is a # poor authoring loop — use `make docs-serve` for that. docs: restart: "no" playwright: build: context: .. dockerfile: frontend/Dockerfile.playwright args: - VITE_API_URL=http://backend:8000 - NODE_ENV=production ipc: host depends_on: - backend - mailcatcher env_file: - ../.env environment: - VITE_API_URL=http://backend:8000 # The suite takes its own origins from these, never from VITE_API_URL — # see frontend/tests/config.ts. - PLAYWRIGHT_API_URL=http://backend:8000 - MAILCATCHER_HOST=http://mailcatcher:1080 - PLAYWRIGHT_HTML_HOST=0.0.0.0 - CI=${CI} volumes: - ../frontend/blob-report:/app/frontend/blob-report - ../frontend/test-results:/app/frontend/test-results ports: - 9323:9323 networks: # Self-contained dev: create a stack-local network instead of expecting the # shared one. compose.local.yml flips this back for the integrated stack. proxy: external: false