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

ResourceMinimumRecommended
Docker24+Latest LTS
Docker Composev2v2.20+
RAM4 GB8 GB (16 GB with Ollama)
Disk20 GB100 GB+ for scan artifacts
CPU2 cores4–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:

  1. Installs Docker if missing
  2. Installs Ollama and pulls qwen2.5-coder:7b
  3. Generates random secrets into .env
  4. Pulls images and starts every service
  5. 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

ScriptWhat it does
npm run db:setupMigrate-deploy (no-op when already applied) + db push + seed. Use for first install or after upgrades.
npm run db:deployJust prisma migrate deploy. Use in CI.
npm run db:pushSync the live schema to schema.prisma without producing a migration file. Dev only.
npm run db:migrateprisma migrate dev — create a new migration during development.
npm run db:seedIdempotent seed (admin org + admin user).
npm run db:generateRegenerate 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"

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
BinaryUnlocks
trivyReal container vulnerability scanning (otherwise inventory only).
cosignKeyless SBOM signing (otherwise RSA fallback only).
svnSubversion source checkouts.
dapperLocal-CLI DAST mode (alternative: HTTP mode).

Observability

Backup

Two stateful services need backup:

Redis state is transient (job queue) and can be lost without data loss.

Air-gapped operation

Troubleshooting

API can't reach the database

Check DATABASE_URL. The Docker stack uses postgresql://pepper:<password>@postgres:5432/pepperpostgres 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.