--- services: db: image: 'jc21/mariadb-aria:latest' container_name: npm-db restart: unless-stopped env_file: .env environment: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} - MYSQL_DATABASE=${MYSQL_DATABASE} - MYSQL_USER=${MYSQL_USER} - MYSQL_PASSWORD=${MYSQL_PASSWORD} - MARIADB_AUTO_UPGRADE=1 volumes: - ./npm/data/mysql:/var/lib/mysql networks: # - proxydb - waf-internal healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u${MYSQL_USER}", "-p${MYSQL_PASSWORD}"] interval: 20s timeout: 5s retries: 5 start_period: 30s app: image: 'jc21/nginx-proxy-manager:latest' container_name: nginx-proxy-manager restart: unless-stopped env_file: .env environment: - TZ=${TZ} - DB_MYSQL_HOST=db - DB_MYSQL_PORT=3306 - DB_MYSQL_USER=${MYSQL_USER} - DB_MYSQL_PASSWORD=${MYSQL_PASSWORD} - DB_MYSQL_NAME=${MYSQL_DATABASE} ports: - '81:81' # Kept so you can access the NPM dashboard on host-ip:81 # '80:80' and '443:443' REMOVED here. They are reachable internally via the network. volumes: - ./npm/data/npm:/data - ./npm/data/letsencrypt:/etc/letsencrypt depends_on: - db networks: # - proxy # - proxydb - waf-internal healthcheck: test: ["CMD", "curl", "-f", "http://localhost:81/api/status"] interval: 20s timeout: 5s retries: 5 start_period: 40s # --------------------------------------------------------------------------- # Caddy + Coraza: WAF engine and reverse proxy # --------------------------------------------------------------------------- caddy-waf: image: ghcr.io/socfortress/waf-caddy:${WAF_IMAGE_TAG:-latest} container_name: caddy-waf ports: - "80:80" - "443:443" # Lets the WAF proxy to apps running on the Docker host (e.g. an nginx # upstream) via http://host.docker.internal:. See demo/README.md. extra_hosts: - "host.docker.internal:host-gateway" volumes: - coraza-rules:/etc/coraza/rules - caddy-config:/etc/caddy - coraza-custom:/etc/coraza/custom - crs-data:/etc/coraza/crs-rules # live CRS dir (seeded by admin-api) - coraza-logs:/var/log/coraza networks: - waf-internal depends_on: http-echo: condition: service_started healthcheck: test: ["CMD", "wget", "-q", "-O-", "http://localhost:2019/config/"] interval: 30s timeout: 10s retries: 3 start_period: 20s deploy: resources: limits: cpus: "1.0" memory: 512M restart: unless-stopped # --------------------------------------------------------------------------- # Dummy upstream — replace with your real protected app(s) # --------------------------------------------------------------------------- http-echo: image: hashicorp/http-echo:0.2.3 container_name: http-echo command: ["-text=upstream-ok"] user: "65534:65534" networks: - waf-internal healthcheck: test: ["NONE"] deploy: resources: limits: cpus: "0.25" memory: 64M restart: unless-stopped # --------------------------------------------------------------------------- # FastAPI Admin API # --------------------------------------------------------------------------- admin-api: image: ghcr.io/socfortress/waf-admin-api:${WAF_IMAGE_TAG:-latest} container_name: admin-api env_file: .env volumes: - tls-certs:/certs # shared TLS cert volume (see admin-ui) # GeoLite2 DB is user-supplied — MaxMind licensing forbids redistribution. # Point GEOIP_DB_PATH at your downloaded GeoLite2-City.mmdb (see README). - ./GeoLite2-City/GeoLite2-City.mmdb:/etc/geoip-bundle/GeoLite2-City.mmdb:ro - geoip-data:/etc/geoip - coraza-rules:/etc/coraza/rules - caddy-config:/etc/caddy - coraza-custom:/etc/coraza/custom - crs-data:/etc/coraza/crs-rules # live CRS dir (seeded from image bundle) - coraza-logs:/var/log/coraza networks: - waf-internal depends_on: postgres: condition: service_healthy redis: condition: service_healthy healthcheck: test: ["CMD", "wget", "-q", "-O-", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 start_period: 30s deploy: resources: limits: cpus: "1.0" memory: 512M restart: unless-stopped # --------------------------------------------------------------------------- # React Admin UI (served by Nginx, HTTPS on 8443) # --------------------------------------------------------------------------- admin-ui: image: ghcr.io/socfortress/waf-admin-ui:${WAF_IMAGE_TAG:-latest} container_name: admin-ui ports: - "8443:8080" volumes: - tls-certs:/etc/nginx/certs # shared with admin-api for cert upload + reload networks: - waf-internal depends_on: admin-api: condition: service_healthy healthcheck: test: ["CMD", "wget", "-q", "--no-check-certificate", "-O-", "https://localhost:8080/"] interval: 30s timeout: 10s retries: 3 start_period: 30s deploy: resources: limits: cpus: "0.5" memory: 128M restart: unless-stopped # --------------------------------------------------------------------------- # PostgreSQL 16 # --------------------------------------------------------------------------- postgres: image: postgres:16.3-alpine container_name: postgres env_file: .env volumes: - postgres-data:/var/lib/postgresql/data networks: - waf-internal healthcheck: test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] interval: 10s timeout: 5s retries: 5 start_period: 10s deploy: resources: limits: cpus: "1.0" memory: 512M restart: unless-stopped # --------------------------------------------------------------------------- # Redis 7 # --------------------------------------------------------------------------- redis: image: redis:7.2.5-alpine container_name: redis user: redis command: ["redis-server", "--appendonly", "yes"] volumes: - redis-data:/data networks: - waf-internal healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 start_period: 5s deploy: resources: limits: cpus: "0.5" memory: 256M restart: unless-stopped # ============================================================================= # Networks # ============================================================================= networks: proxy: external: true proxydb: external: true waf-internal: driver: bridge # ============================================================================= # Named volumes # ============================================================================= volumes: coraza-rules: coraza-custom: coraza-logs: caddy-config: crs-data: geoip-data: postgres-data: redis-data: tls-certs: