# Source: https://oneuptime.com/blog/post/2026-01-07-go-hot-reloading-docker-air/view#step-4-set-up-docker-compose # docker-compose.yml # Development environment with hot reloading services: # Main Go application with Air hot reloading alarm-debug: extra_hosts: - "host.docker.internal:host-gateway" image: alarm:debug container_name: alarm-debug # Cold `docker compose -f docker-compose.debug.yaml up -d` builds this image # directly — no pre-build step needed. Do NOT add `network: host` here: # `docker compose up`/`build` cannot grant the `network.host` build # entitlement (that needs `--allow=network.host`, which only `docker build` # accepts), so a cold up would fail with "additional privileges requested". # The default bridge reaches the dev Gitea (192.168.122.183), so # Dockerfile.debug's GOPROXY resolves shared modules without host networking. build: context: . dockerfile: Dockerfile.debug # `alarm:debug` is a local-only tag: build it, never look it up in a # registry (otherwise a cold `up` warns "pull access denied"). pull_policy: build # Expose ports: host:container ports: # Frozen v1 contract: host 8088 -> container 8080 (single port: API + # /ws/alarm). - "8088:8080" # Delve: unique host port per service (auth-debug 2345, keepnote-debug # 2346, calendar-debug 2347, store 2348), so the Go attach launch config # reaches Air's Delve listener (in-container 2345) from the host. - "2349:2345" # Delve debugger port # Volume mounts - THE KEY TO HOT RELOADING # Mount source code as a volume so changes are reflected immediately volumes: - ./src:/app - go-mod-cache:/go/pkg/mod - go-build-cache:/root/.cache/go-build - /app/src/tmp # Exclude Air's tmp dir from syncing working_dir: /app/ # Environment variables for the application environment: # DCNTRLIZED_DEV is the single dev selector (registry/auth URLs). - DCNTRLIZED_DEV=true # Contract rev 9: internal traffic is plaintext, so the debug selector no # longer gates any listener — this listener is ALWAYS plaintext now. - DCNTRLIZED_DEBUG=true # Shared auth service target (dcntrlized-auth on the dcntrlized network) - AUTH_SERVICE_URL=http://dcntrlized-auth:8080 # Service identity (inert in v1 — no /config edge — kept for fleet parity) - DCNTRLIZED_SERVICE_NAME=alarm # Database connection (shared dcntrlized-db, scram-sha-256, sslmode=disable) - DATABASE_URL=postgres://postgres:dcntrlized@dcntrlized-db:5432/dcntrlized?sslmode=disable # Go specific settings - GO111MODULE=on - GOPROXY=http://192.168.122.183:3000/api/packages/Dcntrlized/go,https://proxy.golang.org,direct - GONOSUMDB=git.dcntrlized.org depends_on: - migrate # Restart policy for development restart: unless-stopped # Network for service communication networks: - dcntrlized_net cap_add: - SYS_PTRACE security_opt: - apparmor:unconfined migrate: image: postgres:16-alpine container_name: alarm-migrate labels: org.dcntrlized.oneoff: "true" networks: - dcntrlized_net volumes: - ./database/migrations:/migrations:ro environment: - PGHOST=dcntrlized-db - PGPORT=5432 - PGUSER=postgres - PGPASSWORD= - PGDATABASE=dcntrlized # Role-agnostic (no -U/-d): Maestro injects the shared credentials/env. command: > sh -c 'until pg_isready -h dcntrlized-db; do sleep 2; done; for f in /migrations/*.sql; do [ -f "$$f" ] || continue; echo "Applying $$f"; psql -v ON_ERROR_STOP=1 -f "$$f" || exit 1; done' # Named volumes for data persistence and caching volumes: # Go module cache - shared across rebuilds go-mod-cache: driver: local # Go build cache - speeds up compilation go-build-cache: driver: local networks: dcntrlized_net: external: true