services: # Internal cache and task broker. It is intentionally not published to the host. # The backend uses it for shared rate-limit storage when # RATELIMIT_STORAGE_URI points at this service (see .env.example). redis: image: redis:7-alpine restart: unless-stopped command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env before starting Compose} volumes: - redis_data:/data networks: - network-scanner # PostgreSQL is optional because SQLite remains the default backend datastore. # Start it explicitly with: docker compose --profile postgres up -d postgres: image: postgres:15-alpine restart: unless-stopped profiles: ["postgres"] environment: POSTGRES_DB: ${POSTGRES_DB:-network_scanner} POSTGRES_USER: ${POSTGRES_USER:-network_scanner} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env before enabling the postgres profile} volumes: - postgres_data:/var/lib/postgresql/data networks: - network-scanner backend: build: context: . dockerfile: Dockerfile.backend restart: unless-stopped environment: FLASK_ENV: production DATABASE_URL: ${DATABASE_URL:-sqlite:////app/data/network_scanner.db} REDIS_URL: redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env before starting Compose}@redis:6379/0 SECRET_KEY: ${SECRET_KEY:?Set a random SECRET_KEY of at least 32 characters in .env before starting Compose} API_ACCESS_TOKEN: ${API_ACCESS_TOKEN:?Set a random API_ACCESS_TOKEN in .env before starting Compose} HOST: 0.0.0.0 REPORTS_DIR: /app/reports CORS_ORIGINS: ${CORS_ORIGINS:-} # The stack runs behind its own nginx proxy: honor the forwarded client # address so per-client rate limits apply instead of one shared bucket. TRUST_PROXY: "1" OPENAI_API_KEY: ${OPENAI_API_KEY:-} OPENAI_MODEL: ${OPENAI_MODEL:-} RATE_LIMIT_DEFAULT: ${RATE_LIMIT_DEFAULT:-} RATE_LIMIT_SCAN: ${RATE_LIMIT_SCAN:-} RATE_LIMIT_REPORT: ${RATE_LIMIT_REPORT:-} RATE_LIMIT_AI: ${RATE_LIMIT_AI:-} RATELIMIT_STORAGE_URI: ${RATELIMIT_STORAGE_URI:-} SCAN_CONCURRENCY: ${SCAN_CONCURRENCY:-} cap_add: - NET_RAW volumes: - ./reports:/app/reports - ./logs:/app/logs - backend_data:/app/data depends_on: - redis networks: - network-scanner frontend: build: context: . dockerfile: Dockerfile.frontend restart: unless-stopped depends_on: - backend networks: - network-scanner # The proxy is the only public service. Put TLS termination in a managed # reverse proxy or load balancer in front of this service for production. nginx: image: nginx:alpine restart: unless-stopped ports: - "127.0.0.1:80:80" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro depends_on: - frontend - backend networks: - network-scanner volumes: redis_data: postgres_data: backend_data: networks: network-scanner: driver: bridge