docker-compose.local-dev.yml
Rama main · réplica de lectura del repositorio original.
# Local development stack with hot-reload
#
# Runs all infrastructure in Docker + backend and web with source mounts.
# Changes to source code trigger automatic rebuilds.
#
# Usage:
# docker compose -f docker-compose.local-dev.yml up -d
#
# First time setup (installs cargo-watch in backend container):
# docker compose -f docker-compose.local-dev.yml up -d --build
#
# View logs:
# docker compose -f docker-compose.local-dev.yml logs -f backend web
#
# Ports:
# Backend API: 8080
# Backend gRPC: 9090
# Web UI: 3000
# Postgres: 30432
# OpenSearch: 9200
# Trivy: 8090
# OpenSCAP: 8091
# Dependency-Track: 8092
# Jaeger UI: 16686
# OTLP gRPC: 4317
services:
postgres:
image: postgres:18-alpine
container_name: artifact-keeper-dev-db
environment:
POSTGRES_USER: registry
POSTGRES_PASSWORD: registry
POSTGRES_DB: artifact_registry
volumes:
- dev_postgres_data:/var/lib/postgresql
- ./docker/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql:ro
ports:
- "30432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U registry -d artifact_registry"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
opensearch:
image: opensearchproject/opensearch:2.19.6
container_name: artifact-keeper-dev-opensearch
environment:
discovery.type: single-node
OPENSEARCH_JAVA_OPTS: "-Xms256m -Xmx256m"
DISABLE_SECURITY_PLUGIN: "true"
DISABLE_INSTALL_DEMO_CONFIG: "true"
volumes:
- dev_opensearch_data:/usr/share/opensearch/data
ports:
- "9200:9200"
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:9200/_cluster/health || exit 1"]
interval: 10s
timeout: 10s
retries: 12
start_period: 30s
restart: unless-stopped
trivy:
image: ghcr.io/aquasecurity/trivy:0.76.0
container_name: artifact-keeper-dev-trivy
command: ["server", "--listen", "0.0.0.0:8090"]
# Air-gapped variant (#5287): add "--skip-db-update" once the cache volume
# holds a DB; see the trivy service in docker-compose.yml.
ports:
- "8090:8090"
volumes:
- dev_trivy_cache:/root/.cache/trivy
- dev_scan_workspace:/scan-workspace:ro
restart: unless-stopped
openscap:
image: ghcr.io/artifact-keeper/artifact-keeper-openscap:latest
container_name: artifact-keeper-dev-openscap
ports:
- "8091:8091"
volumes:
- dev_scan_workspace:/scan-workspace:ro
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8091/health')"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
dependency-track-apiserver:
image: dependencytrack/apiserver:4.14.3
container_name: artifact-keeper-dev-dtrack-api
depends_on:
postgres:
condition: service_healthy
environment:
ALPINE_DATABASE_MODE: external
ALPINE_DATABASE_URL: jdbc:postgresql://postgres:5432/dependency_track
ALPINE_DATABASE_DRIVER: org.postgresql.Driver
ALPINE_DATABASE_USERNAME: registry
ALPINE_DATABASE_PASSWORD: registry
ALPINE_DATA_DIRECTORY: /data
ALPINE_ENFORCE_AUTHENTICATION: "true"
ALPINE_CORS_ENABLED: "true"
ALPINE_CORS_ALLOW_ORIGIN: "*"
# Proxy configuration (required for NVD mirroring behind corporate proxies)
ALPINE_HTTP_PROXY_ADDRESS: ${DTRACK_HTTP_PROXY_ADDRESS:-}
ALPINE_HTTP_PROXY_PORT: ${DTRACK_HTTP_PROXY_PORT:-}
ALPINE_HTTP_PROXY_USERNAME: ${DTRACK_HTTP_PROXY_USERNAME:-}
ALPINE_HTTP_PROXY_PASSWORD: ${DTRACK_HTTP_PROXY_PASSWORD:-}
ALPINE_NO_PROXY: ${DTRACK_NO_PROXY:-localhost,127.0.0.1}
volumes:
- dev_dtrack_data:/data
ports:
- "8092:8080"
restart: unless-stopped
jaeger:
image: jaegertracing/all-in-one:1.62.0
container_name: artifact-keeper-dev-jaeger
environment:
COLLECTOR_OTLP_ENABLED: "true"
ports:
- "16686:16686"
- "4317:4317"
- "4318:4318"
restart: unless-stopped
# Runs on a small shell-bearing image (alpine) rather than the backend
# image: the hardened runtime image (issue #2059) has no /bin/sh — nor the
# `jq` that init-dtrack.sh needs — so it can't host this bootstrap script
# (issue #2084). Mirrors the dtrack-init fix in docker-compose.yml.
dtrack-init:
image: alpine:3.24
container_name: artifact-keeper-dev-dtrack-init
depends_on:
postgres:
condition: service_healthy
dependency-track-apiserver:
condition: service_healthy
volumes:
- ./docker/init-dtrack.sh:/init-dtrack.sh:ro
- dev_shared_config:/shared
environment:
DTRACK_INIT_FORCE_ROTATE: ${DTRACK_INIT_FORCE_ROTATE:-false}
DEPENDENCY_TRACK_ADMIN_PASSWORD: ${DEPENDENCY_TRACK_ADMIN_PASSWORD:-ArtifactKeeper2026!}
entrypoint: ["/bin/sh", "-c"]
command:
- |
apk add --no-cache curl jq >/dev/null 2>&1
exec /bin/sh /init-dtrack.sh
restart: "no"
healthcheck:
disable: true
# Make the shared scan_workspace volume writable by the non-root dev backend
# user before it starts. The dev backend runs as a system `appuser` with a
# runtime-assigned UID, so we make the volume world-writable rather than
# chown to a fixed UID. See docker-compose.yml's scan-workspace-init and
# issue #1563 for the production equivalent.
scan-workspace-init:
image: alpine:3.24
container_name: artifact-keeper-dev-scan-ws-init
user: "0:0"
entrypoint: ["/bin/sh", "-c"]
command:
- |
mkdir -p /scan-workspace
chmod 0777 /scan-workspace
echo "scan-workspace-init: dev volume made writable"
volumes:
- dev_scan_workspace:/scan-workspace
restart: "no"
healthcheck:
disable: true
# Backend with hot-reload using cargo-watch
backend:
build:
context: .
dockerfile: docker/Dockerfile.backend.dev
container_name: artifact-keeper-dev-backend
depends_on:
postgres:
condition: service_healthy
opensearch:
condition: service_healthy
scan-workspace-init:
condition: service_completed_successfully
dtrack-init:
condition: service_completed_successfully
entrypoint:
- /bin/sh
- -c
- cargo watch -x 'run --bin artifact-keeper'
environment:
# Reduce peak rustc memory for this large workspace so the compile
# doesn't get OOM-killed inside the podman VM. line-tables-only keeps
# enough debug info for stack traces/backtraces without full DWARF, and
# capping parallel jobs avoids stacking multiple heavy crates (wasmtime,
# git2, sqlx, tonic) in memory at once. Dev-container only; does not
# affect native builds, CI, or other contributors.
CARGO_PROFILE_DEV_DEBUG: "line-tables-only"
CARGO_BUILD_JOBS: "3"
DATABASE_URL: postgresql://registry:registry@postgres:5432/artifact_registry
STORAGE_PATH: /data/storage
BACKUP_PATH: /data/backups
PLUGINS_DIR: /data/plugins
JWT_SECRET: dev-secret-change-in-production-please
ADMIN_PASSWORD: admin
RUST_LOG: info,artifact_keeper=debug
LOG_FORMAT: ${LOG_FORMAT:-text}
LOG_ANSI: ${LOG_ANSI:-auto}
AUDIT_STREAM: ${AUDIT_STREAM:-off}
ENVIRONMENT: development
CORS_ORIGINS: http://localhost:3000,http://localhost:8080
TRIVY_URL: http://trivy:8090
OPENSCAP_URL: http://openscap:8091
SCAN_WORKSPACE_PATH: /scan-workspace
OPENSEARCH_URL: http://opensearch:9200
DEPENDENCY_TRACK_URL: http://dependency-track-apiserver:8080
DEPENDENCY_TRACK_API_KEY_FILE: /shared/dtrack-api-key
DEPENDENCY_TRACK_ENABLED: "true"
ALLOW_HTTP_INTEGRATIONS: "1"
# Webhooks v2 signing-secret encryption key. 32 bytes, base64 encoded.
# Generate once for your local stack with: openssl rand -base64 32,
# then export AK_WEBHOOK_SECRET_KEY before docker compose up. The
# placeholder below is intentionally invalid base64 so the backend
# ensure_configured check fails fast with a clear error rather than
# silently running with a shared dev key.
AK_WEBHOOK_SECRET_KEY: ${AK_WEBHOOK_SECRET_KEY:-REPLACE_ME_WITH_OUTPUT_OF_openssl_rand_base64_32}
# Webhooks v2 producer is off by default; enable after rotating
# migrated webhook secrets to avoid duplicate delivery from System B.
WEBHOOKS_V2_PRODUCER_ENABLED: ${WEBHOOKS_V2_PRODUCER_ENABLED:-false}
# Forward host proxy settings so the backend can reach external services
# through a corporate proxy. reqwest honours these natively.
HTTP_PROXY: ${HTTP_PROXY:-}
HTTPS_PROXY: ${HTTPS_PROXY:-}
ALL_PROXY: ${ALL_PROXY:-}
# Prevent host proxy settings from intercepting container-to-container
# traffic on the compose network. See docker-compose.yml for details.
no_proxy: "${NO_PROXY:-},localhost,127.0.0.1,db,postgres,opensearch,trivy,openscap,dependency-track-apiserver"
OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317
OTEL_SERVICE_NAME: artifact-keeper
HOST: 0.0.0.0
PORT: 8080
SQLX_OFFLINE: "true"
CARGO_TARGET_DIR: /target
volumes:
# Mount source code for hot-reload
- ./backend:/app/backend:cached
- ./edge:/app/edge:cached
- ./Cargo.toml:/app/Cargo.toml:cached
- ./Cargo.lock:/app/Cargo.lock:cached
- ./.sqlx:/app/.sqlx:cached
# Persistent volumes for faster builds
- dev_cargo_registry:/usr/local/cargo/registry
- dev_cargo_target:/target
- dev_sccache:/sccache
- dev_artifact_storage:/data/storage
- dev_backup_storage:/data/backups
- dev_plugins_storage:/data/plugins
- dev_scan_workspace:/scan-workspace
- dev_shared_config:/shared:ro
ports:
- "8080:8080"
- "9090:9090"
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:8080/health || exit 1"]
interval: 15s
timeout: 5s
retries: 20
start_period: 120s
restart: unless-stopped
# Web frontend with hot-reload using next dev
# Set WEB_PATH env var if artifact-keeper-web is not at ../artifact-keeper-web
web:
build:
context: ${WEB_PATH:-../artifact-keeper-web}
dockerfile: Dockerfile.dev
container_name: artifact-keeper-dev-web
depends_on:
backend:
condition: service_healthy
environment:
NEXT_PUBLIC_API_URL: http://localhost:8080
BACKEND_URL: http://backend:8080
NODE_ENV: development
WATCHPACK_POLLING: "true"
volumes:
# Mount source code for hot-reload
- ${WEB_PATH:-../artifact-keeper-web}/src:/app/src:cached
- ${WEB_PATH:-../artifact-keeper-web}/public:/app/public:cached
- ${WEB_PATH:-../artifact-keeper-web}/next.config.ts:/app/next.config.ts:cached
- ${WEB_PATH:-../artifact-keeper-web}/tailwind.config.ts:/app/tailwind.config.ts:cached
- ${WEB_PATH:-../artifact-keeper-web}/tsconfig.json:/app/tsconfig.json:cached
- ${WEB_PATH:-../artifact-keeper-web}/components.json:/app/components.json:cached
- ${WEB_PATH:-../artifact-keeper-web}/postcss.config.mjs:/app/postcss.config.mjs:cached
# Exclude node_modules (use container's own)
- dev_web_node_modules:/app/node_modules
ports:
- "3000:3000"
restart: unless-stopped
volumes:
dev_postgres_data:
dev_opensearch_data:
dev_trivy_cache:
dev_dtrack_data:
dev_shared_config:
dev_cargo_registry:
dev_cargo_target:
dev_sccache:
dev_artifact_storage:
dev_backup_storage:
dev_plugins_storage:
dev_scan_workspace:
dev_web_node_modules:
networks:
default:
name: artifact-keeper-dev-network