Files
rengine/docker-compose.yml
T
Yogesh OjhaandClaude Opus 5 06fa55824d perf(docker): build the worker image once and keep the go cache out of it
Four compose services declared their own build against worker/Dockerfile, so
buildkit shared the solve and then exported and unpacked the same image once per
tag. Compose does not dedupe identical targets, measured on a three-service
probe. Under the containerd snapshotter the unpack is a second full pass over
every byte, as expensive as the export.

The go module and build caches were baked into layers: `go install nuclei` made
a 2.66GB layer for a 190MB binary, and /root/go/pkg/mod (3.4GB) plus
/root/.cache/go-build (3.3GB) were half the 13.1GB image.

- the tools build in a throwaway stage with --mount=type=cache on GOCACHE and
  GOMODCACHE; the runtime stage copies /out/bin alone
- worker-default builds rengine-worker:latest; worker-beat, ct-stream and
  flower reference it. Building one of those alone now needs worker-default
  built first
- the runtime stage drops the go toolchain, build-essential and libpcap-dev;
  uv sync needs no compiler. libpcap0.8t64 stays, naabu dlopens it for SYN so
  ldd shows nothing and the loss would only surface at scan time
- -ldflags=-s trims the binaries 650MB -> 495MB
- only shared/ and tools/ are copied, being file:// path deps; stages/,
  reports/, interest/, toolbox/, mcp/, connectors/ and binaries/ are mounted
- the frontend fetches tech icons above COPY frontend/. ., so a source edit no
  longer re-runs the tarball download
- .dockerignore excludes .git (271MB) and node_modules (320MB); COPY frontend/.
  . had been copying the host's node_modules over the npm ci result

worker image 13.1GB -> 2.41GB, frontend 1.38GB -> 969MB
worker export+unpack 453s across three tags -> 55s across one
editing stages/ or reports/ now rebuilds nothing (1s), frontend/src 4s,
shared/ 19s; cold build 475s -> 414s, still dominated by go compilation

Tool versions diff identical against the old image, naabu SYN proven against a
live listener, 8/8 services up, 853 tests green, ruff clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015fWjAbvFBdmsmVeMcwaaR9
2026-09-13 19:00:40 +05:30

270 lines
7.4 KiB
YAML

services:
db:
image: postgres:16-alpine
shm_size: ${POSTGRES_SHM_SIZE:-1gb}
env_file:
- .env
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "${BIND_ADDR:-127.0.0.1}:5432:5432"
command: >
postgres
-c shared_buffers=${POSTGRES_SHARED_BUFFERS:-2GB}
-c effective_cache_size=${POSTGRES_EFFECTIVE_CACHE_SIZE:-6GB}
-c work_mem=${POSTGRES_WORK_MEM:-32MB}
-c maintenance_work_mem=${POSTGRES_MAINTENANCE_WORK_MEM:-512MB}
-c random_page_cost=1.1
-c effective_io_concurrency=200
-c max_parallel_workers_per_gather=4
-c jit=off
-c max_connections=${POSTGRES_MAX_CONNECTIONS:-150}
-c shared_preload_libraries=pg_stat_statements
-c pg_stat_statements.max=5000
-c pg_stat_statements.track=top
-c track_io_timing=on
-c log_min_duration_statement=${POSTGRES_LOG_MIN_DURATION:-2000}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-rengine}"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:8-alpine
ports:
- "${BIND_ADDR:-127.0.0.1}:6379:6379"
volumes:
- redis_data:/data
environment:
REDIS_PASSWORD: ${REDIS_PASSWORD:-}
healthcheck:
test:
[
"CMD-SHELL",
"redis-cli $${REDIS_PASSWORD:+-a \"$$REDIS_PASSWORD\"} --no-auth-warning ping | grep -q PONG",
]
interval: 5s
timeout: 5s
retries: 5
command: >
redis-server
--appendonly yes
--maxmemory ${REDIS_MAXMEMORY:-1gb}
--maxmemory-policy noeviction
${REDIS_PASSWORD:+--requirepass} ${REDIS_PASSWORD:-}
api:
build:
context: .
dockerfile: api/Dockerfile
target: development
volumes:
- ./api/app:/app/app
- ./shared:/app/shared
- ./tools:/app/tools
- ./stages:/app/stages
- ./reports:/app/reports
- ./interest:/app/interest
- ./mcp:/app/mcp
- ./connectors:/app/connectors
- ./toolbox:/app/toolbox
- ./binaries:/app/binaries:ro
- ./alembic:/app/alembic
- ./alembic.ini:/app/alembic.ini
- ./tests:/app/tests
- ./pyproject.toml:/app/pyproject.toml
# read-only, so the tests can check a frontend mirror has not drifted
- ./frontend/src/lib/config:/app/frontend-config:ro
- ./scan_media:/app/scan_media
- vuln_templates:/app/vuln-templates
- wordlists:/app/wordlists
- reports_out:/app/reports-out
- exports_out:/app/exports-out
- report_fonts:/app/report-fonts
- /app/shared/build
- /app/shared/rengine_shared.egg-info
- /app/tools/build
- /app/tools/rengine_tools.egg-info
ports:
- "${APP_BIND_ADDR:-0.0.0.0}:8000:8000"
env_file:
- .env
environment:
- WATCHFILES_FORCE_POLLING=true
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
# default workers for handling regular tasks like imports, whois etc
worker-default:
init: true
image: rengine-worker:latest
build:
context: .
dockerfile: worker/Dockerfile
target: development
volumes:
- ./worker/app:/app/app
- ./shared:/app/shared
- ./tools:/app/tools
- ./stages:/app/stages
- ./reports:/app/reports
- ./interest:/app/interest
- ./toolbox:/app/toolbox
- ./scan_media:/app/scan_media
- vuln_templates:/app/vuln-templates
- wordlists:/app/wordlists
- reports_out:/app/reports-out
- exports_out:/app/exports-out
- report_fonts:/app/report-fonts
- /app/shared/build
- /app/shared/rengine_shared.egg-info
- /app/tools/build
- /app/tools/rengine_tools.egg-info
env_file:
- .env
# the widest level holds 5 stages
command: >
uv run celery -A app.celery worker
--loglevel=DEBUG
--concurrency=${CELERY_SCAN_CONCURRENCY:-12}
--queues=critical,default,scans
--hostname=worker-default@%h
--max-tasks-per-child=100
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "/app/.venv/bin/celery -A app.celery inspect ping -d worker-default@$$HOSTNAME || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
# beat scheduler: emits the schedule tick that fires due scan schedules
worker-beat:
init: true
# built by worker-default
image: rengine-worker:latest
volumes:
- ./worker/app:/app/app
- ./shared:/app/shared
- ./tools:/app/tools
- ./stages:/app/stages
- ./reports:/app/reports
- ./interest:/app/interest
- ./toolbox:/app/toolbox
- ./scan_media:/app/scan_media
- vuln_templates:/app/vuln-templates
- wordlists:/app/wordlists
- reports_out:/app/reports-out
- exports_out:/app/exports-out
- report_fonts:/app/report-fonts
- /app/shared/build
- /app/shared/rengine_shared.egg-info
- /app/tools/build
- /app/tools/rengine_tools.egg-info
env_file:
- .env
command: >
uv run celery -A app.celery beat
--loglevel=INFO
--schedule=/tmp/celerybeat-schedule
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
# certificate transparency stream for watched programs
ct-stream:
init: true
# built by worker-default
image: rengine-worker:latest
volumes:
- ./worker/app:/app/app
- ./shared:/app/shared
- ./tools:/app/tools
- ./stages:/app/stages
- ./reports:/app/reports
- ./interest:/app/interest
- ./toolbox:/app/toolbox
- ct_state:/app/ct-state
- /app/shared/build
- /app/shared/rengine_shared.egg-info
- /app/tools/build
- /app/tools/rengine_tools.egg-info
env_file:
- .env
command: uv run python -m app.ct_stream
restart: unless-stopped
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
flower:
# built by worker-default
image: rengine-worker:latest
volumes:
- ./worker/app:/app/app
- ./shared:/app/shared
- ./tools:/app/tools
- ./stages:/app/stages
- ./reports:/app/reports
- ./interest:/app/interest
- ./toolbox:/app/toolbox
- ./scan_media:/app/scan_media
- vuln_templates:/app/vuln-templates
- wordlists:/app/wordlists
- reports_out:/app/reports-out
- exports_out:/app/exports-out
- report_fonts:/app/report-fonts
- /app/shared/build
- /app/shared/rengine_shared.egg-info
- /app/tools/build
- /app/tools/rengine_tools.egg-info
profiles: ["debug"]
ports:
- "${BIND_ADDR:-127.0.0.1}:5555:5555"
env_file:
- .env
command: >
uv run celery -A app.celery flower
--port=5555
--basic-auth=${FLOWER_USER:-admin}:${FLOWER_PASSWORD:-admin}
depends_on:
redis:
condition: service_healthy
frontend:
build:
context: .
dockerfile: frontend/Dockerfile.dev
volumes:
- ./frontend/src:/app/src
- ./frontend/vite.config.ts:/app/vite.config.ts
ports:
- "${APP_BIND_ADDR:-0.0.0.0}:5173:5173"
environment:
- NODE_ENV=development
depends_on:
- api
volumes:
ct_state:
postgres_data:
redis_data:
celery_beat_data:
vuln_templates:
wordlists:
exports_out:
reports_out:
report_fonts: