Docs › Self-Hosted Server

Enterprise

Self-hosted server

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.

How it works — and why it's safe

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.

Prerequisites

Configuration

The server is configured entirely with environment variables:

VariableRequiredDefaultPurpose
PULLGUARD_LICENSE_KEYyesYour Enterprise licence (offline key; validates locally with no network). The server refuses to start below Enterprise.
PULLGUARD_SERVER_INGEST_TOKENSyesComma-separated bearer tokens your CI presents on POST /api/scans. Rotate by adding a new one, cutting CI over, then removing the old.
PORTno8080Listen port.
PULLGUARD_SERVER_HOSTno0.0.0.0Bind address.
PULLGUARD_SERVER_DATA_DIRno./dataDirectory for the database. Mount a volume here to persist history.
PULLGUARD_SERVER_MAX_BODY_BYTESno5242880Hard cap on request body (oversize → HTTP 413).
PULLGUARD_SERVER_RETAIN_SCANSno200Scans 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.

Quick start (Docker Compose)

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.

Wire your CI to upload results

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.

The dashboards

These reuse the same renderers as the air-gapped HTML report — see Reports & Dashboard.

Human login — SSO & RBAC

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:

Sessions are server-side with idle and absolute expiry and CSRF protection. Full SSO setup ships as docs/sso.md in the server package.

Security posture

Prefer not to host anything yet?

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.