Skip to content

Configuration

Every variable is optional and goes in .env (a copy of .env.example). After changing any of them, run docker compose up -d. python -m pitangus check-config (or make cli ARGS=check-config) checks them all; the server and the worker do it on start and stop with a clear message if one is invalid. On other platforms, see deploy.md.

Variable Default Purpose
PITANGUS_HOST_BIND / PITANGUS_HOST_PORT 127.0.0.1 / 8766 Where the panel is published on the host.
PITANGUS_PUBLIC_URL http://127.0.0.1:8766 URL people use to open the panel; it determines Secure cookies, HSTS and the App’s Setup URL.
PITANGUS_ALLOWED_ORIGINS 127.0.0.1 and localhost Accepted origins (Host header and CSRF).
PITANGUS_DEFAULT_LOCALE en en or es. Language for PR comments, notifications, Jira issues, reports and CLI output when no person asked for one. The panel doesn’t use it: it follows the browser’s language, and each person can change it from the sidebar or the sign-in screen.
PITANGUS_MASTER_KEY generated in config/ Master key (openssl rand -base64 32): it encrypts every secret in the database. Required where there is no persistent disk; the same on the API and every worker.
PITANGUS_SESSION_KEY generated, sealed in the database Key that signs session cookies (32 bytes in base64). Only to pin it from a secrets manager.
PITANGUS_REQUIRE_TOTP admins admins, all or none.
PITANGUS_NVD_API_KEY — NVD API key: faster CVE downloads. Sent in a header and never logged.
PITANGUS_DB_PASSWORD (generated) PostgreSQL password; make setup writes it to .env.
PITANGUS_DATABASE_URL (compose) PostgreSQL connection string. compose.yaml builds it from the password; outside compose, e.g. postgresql://pitangus:…@localhost:5432/pitangus (the postgres:// URLs managed databases hand out work as they are).
PITANGUS_DATA_DIR data (CLI) or a temporary folder (ASGI) Rebuildable caches, working folders and optional file logs. Compose sets it to /data; application state still lives in PostgreSQL.
PITANGUS_CONFIG_DIR the platform’s user config folder Folder for master.key when PITANGUS_MASTER_KEY is not set. Compose sets it to /config.
PITANGUS_EMBEDDED_WORKER 1 1: the server also runs the scans (a single process). In compose the API uses 0 and the worker service runs them.
PITANGUS_BIND 127.0.0.1 Interface used by pitangus serve. Compose overrides it inside the container; prefer PITANGUS_HOST_BIND to control host publishing.
PITANGUS_CVE_SYNC on off turns off the local NVD copy.
PITANGUS_EUVD on off stops querying EUVD (ENISA) when NVD hasn’t scored a CVE. Only the CVE identifier is sent.
PITANGUS_PR_POLL_SECONDS 300 How often watched PRs are checked.
PITANGUS_BRANCH_MIN_MINUTES 60 Minimum gap between two automatic rescans of the same repository’s main branch (minimum 10).
PITANGUS_ADVISORY_WATCH_HOURS 24 How many hours between offline checks of already-scanned dependencies against new advisories. 0 turns it off.
PITANGUS_ALLOW_PRIVATE_WEBHOOKS empty 1 allows alerts to webhooks on your internal network (blocked by default to prevent SSRF).
PITANGUS_ALLOW_PRIVATE_REGISTRIES — 1 allows scanning images from registries with a private IP (your internal network). Blocked by default to prevent SSRF.
PITANGUS_TLS_CERT / PITANGUS_TLS_KEY — Certificate and private-key files for TLS without a proxy. Both are required together.
PITANGUS_ALLOW_INSECURE_HTTP empty (off) 1 lets the server start when PITANGUS_PUBLIC_URL is plain http:// on an address other than 127.0.0.1/localhost, which it otherwise refuses. Passwords, session cookies and tokens then cross the network in clear: only on a trusted network, at your own risk. Prefer HTTPS.
PITANGUS_DOWNLOAD_TIMEOUT 900 Seconds a repository archive may take to download from GitHub before the scan gives up (minimum 60). Raise it for very large repositories or slow links.
PITANGUS_API_MEMORY, PITANGUS_WORKER_MEMORY 1g, 2g (4g for the worker in deploy/compose.yaml, where the engines run inside it) Memory ceilings of the API and worker containers in Compose, so a runaway process can’t starve the host and Postgres. The engines’ own containers have theirs (3 GB, 2 CPUs).
DOCKER_SOCKET_GID detected by make Group that owns the Docker socket on Linux and WSL with native Docker (stat -Lc %g /var/run/docker.sock), so the worker can start the engines. Read by Compose, not by the app; set it only if you start with docker compose directly. Not needed on Docker Desktop or OrbStack (group 0, always added).
GITHUB_APP_ID + GITHUB_APP_SLUG + GITHUB_APP_PRIVATE_KEY_FILE — Alternative to the form: mount the App as a deployment secret. Takes precedence over the secret store.
GITHUB_TOKEN / GITLAB_TOKEN — Deployment-provided tokens for code sources. GitHub token connections are supported; the GitLab provider is declared but deliberately disabled until it is tested, so GITLAB_TOKEN does not enable GitLab today. Prefer the GitHub App for GitHub repositories.
OPENAI_API_KEY / ANTHROPIC_API_KEY — Keys the operator can list and validate with the CLI. AI analysis is not implemented and these keys receive no code or findings.
PITANGUS_HOST_CONFIG_DIR ./config Host folder for the master key, when PITANGUS_MASTER_KEY isn’t set.
PITANGUS_HOST_DATA_DIR / PITANGUS_HOST_RULES_DIR detected by Compose Absolute host paths mounted into sibling engine containers. Set them only when automatic Docker mount discovery cannot resolve the bind mounts.
PITANGUS_FORWARDED_ALLOW_IPS empty Behind a reverse proxy that is the only way to reach the API: the proxy addresses whose X-Forwarded-For is believed (* = any peer). Without it, sign-in throttling and the logs see the proxy’s address for everybody. compose.prod.yaml sets it for Caddy.
PITANGUS_ENGINE_RUNNER auto docker: each engine in a sibling container through the Docker socket. local: the engines installed in the worker image (pitangus-worker), no socket. auto: Docker if it answers, else the installed engines.
PITANGUS_PERIODIC leader leader: a worker runs the periodic tasks on its own clock. external: a scheduler triggers them with pitangus periodic or GET /api/cron (deploy.md).
PITANGUS_CRON_TOKEN / CRON_SECRET empty (off) Bearer token for GET /api/cron, only with PITANGUS_PERIODIC=external. At least 32 characters. CRON_SECRET is what Vercel Cron sends.
PITANGUS_LOG_FORMAT text json: one JSON object per line on the process output.
PITANGUS_LOG_LEVEL INFO DEBUG, INFO, WARNING or ERROR. Debug logs are still passed through the secret redactor.
PITANGUS_LOG_FILE empty (Compose: logs/app.log) Also write JSON logs to this file, rotated at 10 MB × 5; a relative path is under the data folder.
PITANGUS_METRICS_TOKEN empty (off) Turns on /api/metrics (Prometheus) for requests with Authorization: Bearer <token>. At least 32 characters: openssl rand -hex 32.
PITANGUS_IMPORT_TOKEN empty (off) Turns on POST /api/ci/sarif, which lets CI import another tool’s SARIF 2.1.0 into an existing asset with Authorization: Bearer <token> (no session). At least 32 characters: openssl rand -hex 32. Also what pitangus import-sarif --server sends, read from the environment.

Server with a domain (deploy-vps.md). Read by Compose, not by the app; make setup DOMAIN=… [PREBUILT=1] [SOCKET=1] writes them (make setup PREBUILT=1, without a domain, only COMPOSE_FILE and PITANGUS_IMAGE).

Variable Default Purpose
PITANGUS_DOMAIN — Domain Caddy gets the certificate for (compose.prod.yaml). The public URL and allowed origins become https://<domain>.
COMPOSE_FILE compose.yaml Compose files every command uses, e.g. compose.yaml:compose.prod.yaml:compose.no-socket.yaml:compose.images.yaml:compose.images.no-socket.yaml. make setup keeps compose.backup-age.yaml if you added it.
PITANGUS_IMAGE — Published image to run instead of building (compose.images.yaml), e.g. ghcr.io/pitangus-dev/pitangus.
PITANGUS_IMAGE_TAG the code’s version Tag of that image; accepts a digest (0.12@sha256:…).
PITANGUS_WORKER_IMAGE_TAG the code’s version The same for <PITANGUS_IMAGE>-worker, the worker with the engines inside (without the socket). make up pins both to the digests it checked.
COMPOSE_PROFILES — backup turns on the scheduled backups service.
PITANGUS_BACKUP_DIR ./backups Where the backup service writes.
PITANGUS_BACKUP_INTERVAL_HOURS / _KEEP_DAYS 24 / 14 How often it backs up, and for how long it keeps its own copies.
PITANGUS_BACKUP_AGE_RECIPIENT — age public keys (age1… or ssh-ed25519 …, comma-separated) to encrypt every backup with, master key included; also read by make backup (needs age on the host). The service needs compose.backup-age.yaml in COMPOSE_FILE. Empty: not encrypted, and without the master key.

Deliberate trade-off: with the repository’s compose.yaml, so you don’t have to install anything but Docker, the worker launches the engines as sibling containers through the Docker socket, which is equivalent to root on the host. On a server, make setup DOMAIN=… (compose.no-socket.yaml) and deploy/compose.yaml use the worker image with the engines inside instead: no socket at all. What each one isolates.