Docs › Self-Hosted Server
Enterprise
Run a live, access-controlled multi-repo PullGuard dashboard
inside your own boundary, on your own domain (e.g.
https://pullguard.yourcompany.com) — with history, SSO and RBAC.
PullGuard's core promise is that your code never leaves your runners. A vendor-hosted dashboard would break that, so instead you run the server:
This is separate from the PullGuard licensing service — that only validates subscriptions and never receives scan data.
The server is configured entirely with environment variables:
| Variable | Required | Default | Purpose |
|---|---|---|---|
PULLGUARD_LICENSE_KEY | yes | — | Your Enterprise licence (offline key; validates locally with no network). The server refuses to start below Enterprise. |
PULLGUARD_SERVER_INGEST_TOKENS | yes | — | Comma-separated bearer tokens your CI presents on POST /api/scans. Rotate by adding a new one, cutting CI over, then removing the old. |
PORT | no | 8080 | Listen port. |
PULLGUARD_SERVER_HOST | no | 0.0.0.0 | Bind address. |
PULLGUARD_SERVER_DATA_DIR | no | ./data | Directory for the database. Mount a volume here to persist history. |
PULLGUARD_SERVER_MAX_BODY_BYTES | no | 5242880 | Hard cap on request body (oversize → HTTP 413). |
PULLGUARD_SERVER_RETAIN_SCANS | no | 200 | Scans retained per repo (bounds growth + history depth). |
The server fails closed: it won't start without a valid Enterprise licence and at least one ingest token — and, if SSO is enabled, not without a valid SSO configuration.
export PULLGUARD_LICENSE_KEY=pullguard_enterprise_yourorg_...
export PULLGUARD_SERVER_INGEST_TOKENS=$(openssl rand -hex 24)
docker compose up -d
# dashboard on http://localhost:8080 (put your TLS-terminating proxy in front)
A Dockerfile, docker-compose.yml, and a Helm chart ship with the server package. For Kubernetes, store the licence and ingest tokens as a secret and helm install the chart.
Add two inputs to your PullGuard GitHub Action — after each scan it uploads the results to your server (best-effort; a server hiccup never fails your build):
- uses: pullguard-dev/pullguard-action@v1
with:
server-url: https://pullguard.yourcompany.com
server-token: ${{ secrets.PULLGUARD_SERVER_TOKEN }}
Set PULLGUARD_SERVER_TOKEN to one of your ingest tokens. The Action uploads only the finding report — never source.
/) — every repo at a glance: project count, failing repos, open-findings and critical/major totals, plus a sortable table. Click a row to drill in./repos/<org>/<name>) — grade, score, findings over time, with drill-down to file:line.These reuse the same renderers as the air-gapped HTML report — see Reports & Dashboard.
By default (PULLGUARD_SSO_MODE=none) the dashboard is protected by your network
boundary / reverse proxy, and machine ingestion is always token-authenticated. To require
human login and role-gating, enable OIDC or
SAML 2.0:
PULLGUARD_SSO_MODE=oidc plus your issuer, client ID and secret, and the server's base URL (Authorization-Code + PKCE).PULLGUARD_SSO_MODE=saml plus your IdP metadata / certificate and entity IDs.viewer < maintainer < admin) via PULLGUARD_SSO_ROLE_MAP. Access is deny-by-default: an authenticated user matching no role gets none.Sessions are server-side with idle and absolute expiry and CSRF protection. Full SSO setup ships as docs/sso.md in the server package.
The scanner already emits pullguard.sarif and a self-contained HTML report on
every run — pipe those into your existing Grafana / SIEM / DefectDojo, or just browse
the offline report. See Reports & Dashboard. The
self-hosted server is the upgrade when you want a live, access-controlled, multi-repo view.
Questions or an Enterprise trial? hello@pullguard.dev.