Self-hosted AI Pentest worker. Run this on a host inside your network when targets sit behind a VPN, on a private network, or under an allow-list that the hosted SaaS scanner can’t reach.

The files

Create these two files in a working directory on the worker host (e.g. ~/tigerstrike), then follow Run Docker below.
docker-compose.client.yml
name: tigerstrike

services:
  tigerstrike:
    image: tigergate/tigerstrike:latest   # or pin a version, e.g. :1.0.0
    container_name: tigerstrike
    entrypoint: ["/usr/local/bin/tigerstrike-service"]
    command: ["--port", "8085", "--workers", "2"]
    network_mode: host                   # required — the worker reaches its sandboxes on the host bridge
    restart: unless-stopped
    stop_grace_period: 30s               # let in-flight scans drain on SIGTERM
    env_file: .env                       # TIGERGATE_API_KEY lives here
    environment:
      TIGERSTRIKE_WORKER_NAME: "${TIGERSTRIKE_WORKER_NAME:-tigerstrike-onprem}"
      TIGERSTRIKE_OUTPUT_ROOT: /var/lib/tigerstrike/runs
      TIGERSTRIKE_ENV: production
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock                       # required — sandbox runs on the host daemon
      - tigerstrike_runs:/var/lib/tigerstrike/runs                      # scan artifacts (dashboard download links)
      - /var/lib/tigerstrike/codebase:/var/lib/tigerstrike/codebase:ro  # optional — private-codebase drop-zone
    mem_limit: 4g
    cpus: 2
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8085/health"]
      interval: 30s
      timeout: 5s
      start_period: 30s
      retries: 3
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "5"

volumes:
  tigerstrike_runs:
.env.example
# Copy to .env, then: chmod 600 .env
# Create the key at: Settings → Organization → API Keys (an Organization key, tg_…)
TIGERGATE_API_KEY=

# Optional — the TigerGate region your organization is in. Leave unset for us1,
# the default. Keys are region-scoped, so this must match the region the key
# above was issued in. See https://docs.tigergate.dev/platform/regions
# TIGERGATE_REGION=

# Optional — an explicit platform URL for a self-hosted or staging install.
# Overrides TIGERGATE_REGION entirely; leave unset for TigerGate SaaS.
# TIGERGATE_BACKEND_URL=

# Optional — friendly name shown in the dashboard's worker fleet view.
# Defaults to "tigerstrike-onprem"; use e.g. "acme-prod-dc1" so multiple hosts don't collide.
# TIGERSTRIKE_WORKER_NAME=acme-prod-dc1

Prerequisites

  • Linux VM with 8 vCPU / 16 GB RAM / 40 GB disk — see Requirements.
  • Docker 24+ and the Docker Compose v2 plugin (docker compose ...).
  • Outbound HTTPS (443) to your region’s API host and Docker Hub — see Outbound endpoints.
  • A TigerGate dashboard account with permission to create API keys.
The worker takes its region from TIGERGATE_REGION in the .env above. Leave it unset for us1, the default, whose API host is api.tigergate.dev. See Regions.
You do NOT need to expose any inbound ports from the public internet. The worker is poll-based — it dials out to the platform and pulls jobs from the queue.

End-to-end setup flow

#StepWhere
1Allow outbound HTTPS to your region’s API host and Docker Hub on the worker hostNetwork / firewall
2Sign in to the TigerGate dashboardBrowser
3Configure your LLM provider key (OpenAI / Anthropic / etc.) and test itDashboard → Pentest → Settings → AI Providers
4Create an Organization API key, copy the tg_… valueDashboard → Settings → Organization → API Keys
5Create the two files above on the worker host; paste the key into .envWorker host (shell)
6docker compose -f docker-compose.client.yml up -d and verify via logsWorker host (shell)
7Confirm the worker shows up in Pentest → Scanners (/pentest/scanners)Dashboard
8Create a target + scan config and pick your self-hosted worker as the runnerDashboard → Pentest → New Scan
9Trigger the scan; findings stream into Pentest → ScansDashboard
Key invariant: the worker host only ever talks outbound to its region’s API host and to your scan target. The platform pushes work to it via long-poll; nothing inbound from the public internet is required.

Run Docker

1

Create the files

Save docker-compose.client.yml and .env.example (from The files above) into a working directory:
mkdir -p ~/tigerstrike && cd ~/tigerstrike
# create docker-compose.client.yml and .env.example here
2

Create your env file from the template

Mode 600 keeps the token out of ps and group-readable paths:
cp .env.example .env
chmod 600 .env
3

Paste your TIGERGATE_API_KEY into .env

$EDITOR .env
Token format: TIGERGATE_API_KEY=tg_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. See §4 below if you don’t have one yet.
4

Pull the image and bring the worker up

docker compose -f docker-compose.client.yml pull
docker compose -f docker-compose.client.yml up -d
5

Verify the worker registered with the platform

docker compose -f docker-compose.client.yml logs -f tigerstrike
You should see a line like worker registered as "tigerstrike-onprem" within ~10 seconds. The worker also appears in the dashboard at Pentest → Scanners (/pentest/scanners).
Health check:
curl -s http://127.0.0.1:8085/health
The worker uses network_mode: host, so the port is on the host directly. Upgrades: bump the image tag in docker-compose.client.yml and re-run docker compose -f docker-compose.client.yml up -d. In-flight scans drain on a 30-second SIGTERM grace period. Stop / remove:
docker compose -f docker-compose.client.yml down       # stop
docker compose -f docker-compose.client.yml down -v    # stop + delete scan artifacts

Run on Kubernetes

Prefer a cluster? The worker runs as a single Deployment with a privileged docker:dind sidecar — managed clusters (EKS / GKE / AKS) run containerd, not Docker, so the sidecar hosts the per-scan sandboxes (both containers mount the volumes at identical paths).
1

Create the namespace + secret

kubectl create namespace tigerstrike
kubectl -n tigerstrike create secret generic tigerstrike-api \
  --from-literal=TIGERGATE_API_KEY=tg_<org-api-key>
2

Apply the manifest

Save the block below as tigerstrike.yaml, then:
kubectl apply -f tigerstrike.yaml
kubectl -n tigerstrike rollout status deploy/tigerstrike
kubectl -n tigerstrike logs -f deploy/tigerstrike -c tigerstrike
tigerstrike.yaml
apiVersion: apps/v1
kind: Deployment
metadata: { name: tigerstrike, namespace: tigerstrike, labels: { app: tigerstrike } }
spec:
  replicas: 1
  strategy: { type: Recreate }
  selector: { matchLabels: { app: tigerstrike } }
  template:
    metadata: { labels: { app: tigerstrike } }
    spec:
      terminationGracePeriodSeconds: 30
      containers:
        - name: tigerstrike
          image: tigergate/tigerstrike:latest   # or pin a version, e.g. :1.0.0
          imagePullPolicy: Always
          command: ["/usr/local/bin/tigerstrike-service"]
          args: ["--port", "8085", "--workers", "2"]
          env:
            - { name: DOCKER_HOST, value: "tcp://127.0.0.1:2375" }   # talk to the DinD sidecar
            - { name: TIGERSTRIKE_WORKER_NAME, value: "acme-eks" }
            - { name: TIGERSTRIKE_BOOT_TIMEOUT, value: "15m" }       # first sandbox pull is ~600 MB
            - name: TIGERGATE_API_KEY
              valueFrom: { secretKeyRef: { name: tigerstrike-api, key: TIGERGATE_API_KEY } }
          ports: [{ containerPort: 8085, name: health }]
          startupProbe: { httpGet: { path: /health, port: 8085 }, periodSeconds: 5, failureThreshold: 24 }
          livenessProbe: { httpGet: { path: /health, port: 8085 }, periodSeconds: 30, failureThreshold: 6 }
          resources: { requests: { cpu: "1", memory: "2Gi" }, limits: { cpu: "2", memory: "4Gi" } }
          volumeMounts:
            - { name: runs, mountPath: /var/lib/tigerstrike/runs }
        - name: dind
          image: docker:24-dind
          securityContext: { privileged: true }
          env: [{ name: DOCKER_TLS_CERTDIR, value: "" }]
          args: ["--host=unix:///var/run/docker.sock"]
          startupProbe: { tcpSocket: { port: 2375 }, periodSeconds: 5, failureThreshold: 24 }
          resources: { requests: { cpu: "1", memory: "2Gi" }, limits: { cpu: "4", memory: "8Gi" } }
          volumeMounts:
            - { name: dind-storage, mountPath: /var/lib/docker }
            - { name: runs, mountPath: /var/lib/tigerstrike/runs }   # same path in both containers
      volumes:
        # Node-local disk (emptyDir) — no PVC / StorageClass needed. Ephemeral:
        # wiped when the pod is rescheduled; the sandbox image re-pulls and run
        # artifacts upload to the platform per-scan, so only an in-flight scan is
        # lost on a mid-scan restart.
        - { name: runs, emptyDir: { sizeLimit: 10Gi } }
        - { name: dind-storage, emptyDir: { sizeLimit: 20Gi } }
This variant keeps all state on the node’s local disk (emptyDir) — no PVC or StorageClass needed, and it’s the right choice when your default StorageClass is a network filesystem (EFS / NFS / Azure Files), where DinD’s overlay store is slow and races. Schedule it onto a node with ~30 GB free ephemeral storage. DinD needs a privileged pod — if Pod Security blocks it, label the namespace: kubectl label ns tigerstrike pod-security.kubernetes.io/enforce=privileged.

Create a target and scan config

Targets and scan configs are created in the TigerGate dashboard — not on the worker host. The worker just executes whatever the platform sends it.
1

Sign in

Go to your region’s dashboard — app.tigergate.dev on us1 — or your private dashboard URL.
2

Open Pentest → New Scan

Pick a target type:
  • Web app — full-app crawl with auth
  • API — REST / GraphQL / SOAP, with optional bearer / OAuth
  • Codebase — scan a repo on disk for code-aware findings
3

Set target details

FieldNotes
Target URLPublic: https://staging.example.com. Internal: any URL the worker host can reach (https://10.0.5.12:8443).
Private codebaseDrop the repo on the worker host at /var/lib/tigerstrike/codebase/<name> and reference that path. (Public Git URLs the worker clones itself.)
Scopeauth, api, auth+api, or full
Max durationWall-clock cap in minutes
Per-scan budget (USD)Hard ceiling for LLM spend on this scan
RunnerPick your self-hosted worker
4

Start scan

Click Start scan. Findings stream into Pentest → Scans → <your scan> as they come in.
Reusable scan configs: save common configurations under Pentest → Scan Configs and reuse them for scheduled scans or CI triggers.

Where to configure the LLM API key

The LLM API key is configured once per organization in the dashboard — not on the worker host. The platform injects it into each scan job at runtime, so the worker host never has the LLM key on disk.
1

Open Pentest → Settings → AI Providers

Click Add provider and pick one of:
  • OpenAI (gpt-4o, gpt-4o-mini, …)
  • Anthropic (claude-opus-4-x, claude-sonnet-4-x, …)
  • Google Gemini
  • Self-hosted / OpenAI-compatible endpoint (set the base URL)
  • Azure OpenAI (set deployment name)
2

Paste your provider API key and click Test

TigerGate makes a tiny live call to confirm it works. Failed tests surface the upstream provider’s error so you can fix it (bad key, missing model access, etc.).
3

(Optional) Set a monthly spend budget

Under Pentest → Settings → Budgets so a runaway scan can’t burn through your quota. See Budgets & spend.
The provider key is stored encrypted at rest in TigerGate. It is sent to the worker only as part of an active scan job and never written to disk on the worker host.

How to get a TigerGate API key

The worker authenticates to the platform with an Organization API key (tg_…).
1

Open the API Keys page

Dashboard → Settings → Organization → API Keys, on the Organization tab.
2

Create a key

Create a key with a name that identifies this host, e.g. acme-prod-dc1.
3

Copy the value into .env

Copy the tg_… value and paste it into .env on the worker host:
TIGERGATE_API_KEY=tg_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Optionally set a friendly name for the worker fleet view:
TIGERSTRIKE_WORKER_NAME=acme-prod-dc1
Rotation: create a new key, update .env, run docker compose -f docker-compose.client.yml up -d, then delete the old key in the dashboard. The worker reconnects with no scan loss. Scope: the key is scoped to your organization. See API keys.