Skip to content

Setup & Configuration

This page covers prerequisites, the .env configuration reference, and running the stack locally for development.

Deploying to a server? Follow the step-by-step Deployment Guide instead — it covers hardware requirements, DNS, Docker installation, and production configuration with copy-paste commands.

Prerequisites

  • Docker and Docker Compose v2
  • Python 3.12+ (for local development only)
  • A dhi.io registry login — the base images (PostgreSQL, RabbitMQ, Python, Caddy, bun, Grype) are pulled from Docker Hardened Images, a private registry, so you must docker login dhi.io before the first build/pull

Environment

Copy the example environment file and review the variables:

cp .env.example .env

Generating strong secrets

For anything other than local development, replace the default SECRET_KEY and the database/broker passwords:

python3 -c "import secrets; print(secrets.token_urlsafe(48))"   # SECRET_KEY
python3 -c "import secrets; print(secrets.token_urlsafe(24))"   # POSTGRES_PASSWORD
python3 -c "import secrets; print(secrets.token_urlsafe(24))"   # RABBITMQ_PASSWORD

For a full picture of every secret, how to store them (.env, Docker secrets, Kubernetes Secrets) and how to rotate them, see Secrets Management.

Variable reference

Variable Default Description
COMPOSE_FILE docker-compose.development.yml Compose entry point. Set to docker-compose.remote.yml for the remote stack
BUILD_TARGET development Docker build target (development / production)
PULL_POLICY always Image pull policy: always pulls on every up, missing pulls only if the image is absent locally
POSTGRES_HOST postgres Database hostname
POSTGRES_PORT 5432 Database port
POSTGRES_USER argus Database user
POSTGRES_PASSWORD argus Database password
POSTGRES_DB argus Database name
RABBITMQ_HOST rabbitmq Celery broker hostname
RABBITMQ_PORT 5672 Celery broker port
RABBITMQ_USER argus Broker user
RABBITMQ_PASSWORD argus Broker password
RABBITMQ_VHOST (empty) Broker vhost
SECRET_KEY change-me-to-a-random-secret Change this. Used for session signing. The app refuses to start if this is left at the default when APP_ENV != development
APP_ENV development development / demo / production (also used as the Docker image tag). Non-development requires a strong SECRET_KEY
APP_VERSION (latest release tag) GHCR image tag used by the remote stack (docker-compose.remote.yml)
LOG_LEVEL info Application log level
LOG_FORMAT json Structured log output format: json (default, single-line JSON) or text
GRPC_PORT 50051 gRPC server port
ALLOWED_HOSTS * Comma-separated Host headers accepted by the TrustedHostMiddleware (* = any host; pin to your public host in production, e.g. argus.example.com). Also reused by the MCP endpoint for DNS-rebinding protection
MCP_ENABLED false Serve the read-only AI-agent MCP endpoint at /api/v1/mcp (see AI Agents (MCP))
SMTP_HOST (empty) SMTP server (Mailpit in dev: mailpit, port 1025)
SMTP_PORT 587 SMTP port
SMTP_USER (empty) SMTP user
SMTP_PASSWORD (empty) SMTP password
SMTP_FROM noreply@argus.local Sender address for outgoing mail
SLACK_WEBHOOK_URL (empty) Slack incoming webhook for alerts
DISCORD_WEBHOOK_URL (empty) Discord incoming webhook for alerts
VULN_RESCAN_INTERVAL_SECONDS 43200 Seconds between automatic vulnerability rescans of the latest SBOMs (12h default; 6h = 21600, 24h = 86400)
ADMIN_EMAIL admin@argus.local Admin user created on first start
LOGIN_TOKEN_EXPIRE_MINUTES 15 Email code validity
SESSION_MAX_AGE_HOURS 1 Session cookie lifetime
API_KEY_TTL_DAYS 90 Default lifetime (days) of new API keys (forced rotation); 0 = no expiry
SHOW_LOGIN_CODE_IN_RESPONSE false Show the one-time login code directly on the login page (dev/demo). Rejected when APP_ENV=production
DISPLAY_TIMEZONE UTC Timezone for UI dates
DOMAIN (empty) Public domain for TLS (Caddy + Let's Encrypt)
LETSENCRYPT_EMAIL admin@argus.local Email for Let's Encrypt
APP_MEM_LIMIT 512M App memory limit (remote stack)
APP_CPU_LIMIT 1.0 App CPU limit (remote stack)
WORKER_MEM_LIMIT 1024M Worker memory limit (remote stack)
WORKER_CPU_LIMIT 2.0 Worker CPU limit (remote stack)
WORKER_CONCURRENCY 2 Concurrent Grype scans per worker (lower = less peak memory)
SCHEDULER_MEM_LIMIT 128M Scheduler memory limit (remote stack)
SCHEDULER_CPU_LIMIT 0.5 Scheduler CPU limit (remote stack)
PROXY_MEM_LIMIT 128M Proxy memory limit (remote stack)
PROXY_CPU_LIMIT 0.5 Proxy CPU limit (remote stack)
LOG_MAX_SIZE 10m Max size per container log file
LOG_MAX_FILE 3 Number of log files kept per container

Local Development

Start the stack

# Log in to the Docker Hardened Images registry (once, before the first build)
docker login dhi.io -u <user>

# Build and start all services
docker compose up -d --build

# Watch app logs
docker compose logs -f app

The base images are pulled from Docker Hardened Images (dhi.io), a private registry. Log in once before the first docker compose up/build; otherwise image pulls fail with an authorization error.

Migrations run automatically on first startup via the container entrypoint; for manual control you can also run:

docker compose exec app alembic upgrade head

Services

Service Port URL
App (FastAPI) 8000 http://localhost:8000 (API docs: /api/docs)
gRPC 50051 —
PostgreSQL 5432 —
RabbitMQ 5672, 15672 http://localhost:15672
Mailpit 1025, 8025 http://localhost:8025
Worker — Celery worker
Scheduler — Celery beat

Stop the stack

docker compose down          # stop and remove containers
docker compose down -v       # also delete volumes (postgres + rabbitmq data)

First Login

  1. Open http://localhost:8000
  2. Enter admin@argus.local (or your ADMIN_EMAIL)
  3. Check Mailpit for the one-time code (or your email inbox in production). With SHOW_LOGIN_CODE_IN_RESPONSE=true (dev/demo only) the code is also shown directly on the login page
  4. Enter the code to sign in

API Authentication

For programmatic access, generate an API key from Settings → Generate Key. Pass it in the Authorization: Bearer header:

curl -H "Authorization: Bearer argus_xxxxxxxxxxxx" http://localhost:8000/api/v1/projects

Keys expire after API_KEY_TTL_DAYS (90 by default) to force rotation; an expired key returns 401. Set a custom lifetime per key when generating it, or create non-expiring keys by setting API_KEY_TTL_DAYS=0.

What's Next