Deployment
Pepper ships as a Docker-Compose stack (API + worker + Postgres + Redis + MinIO) plus an optional Ollama service. The database schema applies itself on container startup, so day-two operations are pull → up.
System requirements
| Resource | Minimum | Recommended |
|---|---|---|
| Docker | 24+ | Latest LTS |
| Docker Compose | v2 | v2.20+ |
| RAM | 4 GB | 8 GB (16 GB with Ollama) |
| Disk | 20 GB | 100 GB+ for scan artifacts |
| CPU | 2 cores | 4–8 cores |
Optional dependencies installed on the worker:
trivy (container scanning),
cosign (keyless signing),
svn (Subversion checkouts),
dapper CLI (when not using the HTTP DAST mode).
Quickstart — Docker (recommended)
mkdir pepper && cd pepper
# Copy setup.sh, docker-compose.yml, .env.example into this directory
chmod +x setup.sh
./setup.sh
The setup script:
- Installs Docker if missing
- Installs Ollama and pulls
qwen2.5-coder:7b - Generates random secrets into
.env - Pulls images and starts every service
- Prints the admin login on stdout
Skip Ollama (smaller boxes) with:
./setup.sh --no-ollama
OLLAMA_MODEL=qwen2.5-coder:3b ./setup.sh
Manual install
# 1. Install dependencies
npm install
# 2. Configure env
cp .env.example .env
$EDITOR .env
# 3. Start infrastructure
docker compose up -d postgres redis minio
# 4. Apply schema + seed admin
npm run db:setup # migrate deploy + db push + seed (all idempotent)
# 5. Start the web server
npm run dev # http://localhost:3000
# 6. In a separate terminal, start the worker
npm run worker
Login with the credentials from .env
(default: admin@pepper.local / pepper-admin-changeme).
Database schema lifecycle
Pepper uses Prisma
Migrate in production. The Docker API container's entrypoint
(scripts/docker-entrypoint-api.sh) runs both
prisma migrate deploy and
prisma db push before starting Next.js, so the schema is always in
sync regardless of how the DB was originally provisioned.
Available scripts
| Script | What it does |
|---|---|
npm run db:setup | Migrate-deploy (no-op when already applied) + db push + seed. Use for first install or after upgrades. |
npm run db:deploy | Just prisma migrate deploy. Use in CI. |
npm run db:push | Sync the live schema to schema.prisma without producing a migration file. Dev only. |
npm run db:migrate | prisma migrate dev — create a new migration during development. |
npm run db:seed | Idempotent seed (admin org + admin user). |
npm run db:generate | Regenerate the Prisma client into src/generated/prisma. |
Migrations folder
Tracked migrations live under prisma/migrations/. The included
migration 20260517_cicd_security_features is idempotent (uses
ADD VALUE IF NOT EXISTS / ADD COLUMN IF NOT EXISTS /
CREATE TABLE IF NOT EXISTS), so it applies cleanly whether your DB
was previously synced via db push or starts fresh.
Upgrading
cd pepper
docker compose pull
docker compose up -d
The API container will run schema sync on boot. Worker restarts pick up the new Prisma client automatically (it's baked into the image).
Worker scaling
BullMQ is the job queue. Scale by raising the worker replica count in
docker-compose.yml:
sast-worker:
deploy:
replicas: 4
environment:
WORKER_CONCURRENCY: "4"
MAX_LLM_CONCURRENCY: "10"
WORKER_CONCURRENCY— concurrent scans per worker process.MAX_LLM_CONCURRENCY— concurrent LLM batches; tune to your provider's rate limits.
Optional worker tools
Some features unlock when extra binaries are present on the worker image. Edit
the worker stage of Dockerfile to add them:
FROM base AS worker
RUN apk add --no-cache git unzip subversion \
&& apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community trivy \
&& wget -q -O /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 \
&& chmod +x /usr/local/bin/cosign
| Binary | Unlocks |
|---|---|
trivy | Real container vulnerability scanning (otherwise inventory only). |
cosign | Keyless SBOM signing (otherwise RSA fallback only). |
svn | Subversion source checkouts. |
dapper | Local-CLI DAST mode (alternative: HTTP mode). |
Observability
- Worker logs are structured JSON (pino) — pipe to your log aggregator.
- Scan progress is written to
Scan.scannerProgressas JSONB and surfaced live in the UI. - Audit log is queryable via
GET /api/audit-log. - Trend metrics:
GET /api/dashboard/trends?days=30.
Backup
Two stateful services need backup:
- Postgres —
pg_dumpthepepperdatabase. - MinIO —
mc mirrorthepepper-artifactsbucket (holds scan logs, SBOMs, signatures).
Redis state is transient (job queue) and can be lost without data loss.
Air-gapped operation
- Set
vulnDbMode = mirrororofflinein Settings → LLM Config to disable OSV egress. - Point
osvApiUrlat an internal OSV mirror if you maintain one. - Use Ollama (local LLM) instead of an external API.
- The pre-commit and CI templates only need network to reach the Pepper API itself; no egress to OSV / OpenAI is required from developer machines.
Troubleshooting
API can't reach the database
Check DATABASE_URL. The Docker stack uses
postgresql://pepper:<password>@postgres:5432/pepper —
postgres is the service name, not localhost.
Worker says "MinIO connection refused"
Set MINIO_ENDPOINT=minio when running under Compose, or
localhost when running outside Docker.
LLM scans never finish
Lower MAX_LLM_CONCURRENCY — most rate limits are per-second, not
per-minute. Check Settings → LLM Config shows the right base URL and
model.
Migration won't apply
The shipped migration is idempotent, but if Prisma's
_prisma_migrations table thinks something is in-flight, run
npx prisma migrate resolve --applied 20260517_cicd_security_features
to mark it applied, then restart the container.