Document Version: 1.0 Date: 2026-03-24 Status: Approved
Leakwatch is available as a container image from GitHub Container Registry. No installation required -- just docker run.
# Pull the latest image
docker pull ghcr.io/hodetech/leakwatch:latestdocker run --rm -v "$(pwd):/scan" ghcr.io/hodetech/leakwatch:latest scan fs /scandocker run --rm ghcr.io/hodetech/leakwatch:latest scan git https://github.com/org/repo.gitdocker run --rm -v "/path/to/repo:/scan" ghcr.io/hodetech/leakwatch:latest scan git /scandocker run --rm ghcr.io/hodetech/leakwatch:latest scan image nginx:latestdocker run --rm \
-e AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY \
-e AWS_REGION=us-east-1 \
ghcr.io/hodetech/leakwatch:latest scan s3 my-bucket --prefix prefix/docker run --rm \
-v "$HOME/.config/gcloud:/home/leakwatch/.config/gcloud:ro" \
ghcr.io/hodetech/leakwatch:latest scan gcs my-bucket --prefix prefix/Every scan prints a summary to stderr showing source, duration, file count, and findings count. When writing output to a file with --output, the summary still appears in the terminal:
docker run --rm -v "$(pwd):/scan:ro" ghcr.io/hodetech/leakwatch:latest scan fs /scan── Scan Summary ─────────────────────────────────
Date: 2026-04-08 14:22:00
Source: filesystem
Target: /scan
Files scanned: 934
Duration: 1.87s
Findings: 2
─────────────────────────────────────────────────
When scanning local files or repositories, you must mount the host directory into the container using the -v flag.
flowchart LR
subgraph Host["Host Machine"]
H1["/path/to/project"]
H2["Output file"]
end
subgraph Container["Docker Container"]
C1["/scan"]
C2["/output"]
LW["leakwatch"]
end
H1 -->|"-v /path/to/project:/scan"| C1
C1 --> LW
LW --> C2
C2 -->|"-v /tmp/results:/output"| H2
For filesystem scans, mount the source directory as read-only for an additional layer of security:
docker run --rm \
-v "/path/to/project:/scan:ro" \
ghcr.io/hodetech/leakwatch:latest scan fs /scanTo save scan results to a file on the host, mount an output directory:
docker run --rm \
-v "$(pwd):/scan:ro" \
-v "/tmp/results:/output" \
ghcr.io/hodetech/leakwatch:latest scan fs /scan \
--format sarif --output /output/results.sarifThe results file will be available at /tmp/results/results.sarif on the host.
When scanning remote Git repositories, no volume mount is needed. Leakwatch clones the repository inside the container:
# Full history scan
docker run --rm ghcr.io/hodetech/leakwatch:latest \
scan git https://github.com/org/repo.git
# Shallow clone for faster scanning
docker run --rm ghcr.io/hodetech/leakwatch:latest \
scan git https://github.com/org/repo.git --depth 50
# Specific branch
docker run --rm ghcr.io/hodetech/leakwatch:latest \
scan git https://github.com/org/repo.git --branch mainFor private repositories that require authentication, pass credentials via environment variables or mount an SSH key:
# HTTPS with token embedded in the URL.
# Leakwatch uses go-git, which reads credentials from the URL's user-info part;
# it does NOT honor git-CLI variables such as GIT_ASKPASS / GIT_PASSWORD.
# The display URL is credential-stripped in logs and output.
docker run --rm \
ghcr.io/hodetech/leakwatch:latest \
scan git https://x-access-token:ghp_xxxxxxxxxxxx@github.com/org/private-repo.git
# SSH key mount
docker run --rm \
-v "$HOME/.ssh:/home/leakwatch/.ssh:ro" \
ghcr.io/hodetech/leakwatch:latest \
scan git git@github.com:org/private-repo.gitTip: Embedding the token in the command line can expose it in shell history and process listings. In CI, prefer injecting it through a masked secret, e.g.
scan git https://x-access-token:${GIT_TOKEN}@github.com/org/private-repo.git.
Leakwatch scans container images using go-containerregistry and does not require a running Docker daemon.
No special configuration needed. Leakwatch pulls and analyzes image layers directly:
# Docker Hub
docker run --rm ghcr.io/hodetech/leakwatch:latest scan image nginx:latest
# GHCR
docker run --rm ghcr.io/hodetech/leakwatch:latest scan image ghcr.io/org/app:v1.2.3
# ECR
docker run --rm ghcr.io/hodetech/leakwatch:latest scan image \
123456789.dkr.ecr.eu-west-1.amazonaws.com/my-app:latestThe scan image command uses go-containerregistry's remote.Image to pull images directly from registries. It does not interact with a local Docker daemon, so scanning a locally-built image requires pushing it to a registry first (or using a local registry such as registry:2):
# Option 1: push to a local registry and scan from there
docker run -d -p 5000:5000 --name registry registry:2
docker tag my-local-app:dev localhost:5000/my-local-app:dev
docker push localhost:5000/my-local-app:dev
leakwatch scan image localhost:5000/my-local-app:dev
# Option 2: scan directly from GHCR or another remote registry after pushing
docker tag my-local-app:dev ghcr.io/myorg/my-local-app:dev
docker push ghcr.io/myorg/my-local-app:dev
leakwatch scan image ghcr.io/myorg/my-local-app:devNote: Mounting the Docker socket (
/var/run/docker.sock) is not needed and not supported by the container source. The daemonless design is intentional — it avoids the privilege escalation risk associated with Docker socket access.
For private registries, mount your Docker config:
docker run --rm \
-v "$HOME/.docker/config.json:/home/leakwatch/.docker/config.json:ro" \
ghcr.io/hodetech/leakwatch:latest scan image registry.example.com/team/service:mainPass AWS credentials for S3 scanning or AWS secret verification:
docker run --rm \
-e AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY \
-e AWS_SESSION_TOKEN \
-e AWS_REGION=us-east-1 \
-v "$(pwd):/scan:ro" \
ghcr.io/hodetech/leakwatch:latest scan fs /scanAlternatively, mount the AWS credentials file:
docker run --rm \
-v "$HOME/.aws:/home/leakwatch/.aws:ro" \
-v "$(pwd):/scan:ro" \
ghcr.io/hodetech/leakwatch:latest scan fs /scanFor GCS scanning, mount the Application Default Credentials or set the service account key:
# Application Default Credentials
docker run --rm \
-v "$HOME/.config/gcloud:/home/leakwatch/.config/gcloud:ro" \
ghcr.io/hodetech/leakwatch:latest scan gcs my-bucket
# Service account key
docker run --rm \
-e GOOGLE_APPLICATION_CREDENTIALS=/creds/sa-key.json \
-v "/path/to/sa-key.json:/creds/sa-key.json:ro" \
ghcr.io/hodetech/leakwatch:latest scan gcs my-bucket# Disable verification (no outbound API calls)
docker run --rm \
-v "$(pwd):/scan:ro" \
ghcr.io/hodetech/leakwatch:latest scan fs /scan --no-verify
# Only show verified active secrets
docker run --rm \
-e AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY \
-v "$(pwd):/scan:ro" \
ghcr.io/hodetech/leakwatch:latest scan fs /scan --only-verifiedservices:
leakwatch:
image: ghcr.io/hodetech/leakwatch:latest
volumes:
- ./:/scan:ro
- ./reports:/output
command: scan fs /scan --format sarif --output /output/results.sarifRun with:
docker compose run --rm leakwatchservices:
leakwatch:
image: ghcr.io/hodetech/leakwatch:latest
volumes:
- ./:/scan:ro
- ./reports:/output
environment:
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- AWS_REGION=us-east-1
command: scan git /scan --only-verified --format json --output /output/results.jsonservices:
scan-api:
image: ghcr.io/hodetech/leakwatch:latest
volumes:
- ../api-service:/scan:ro
- ./reports:/output
command: scan fs /scan --format json --output /output/api-results.json
scan-web:
image: ghcr.io/hodetech/leakwatch:latest
volumes:
- ../web-app:/scan:ro
- ./reports:/output
command: scan fs /scan --format json --output /output/web-results.jsonRun all scans in parallel:
docker compose up --abort-on-container-exitname: Secret Scan
on: [push, pull_request]
jobs:
leakwatch:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run Leakwatch
run: |
docker run --rm \
-v "${{ github.workspace }}:/scan:ro" \
ghcr.io/hodetech/leakwatch:latest \
scan git /scan --since-commit HEAD~1 \
--only-verified --min-severity high \
--format sarif --output /dev/stdout > results.sarif
- name: Upload SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarifsecret-scan:
stage: test
image: ghcr.io/hodetech/leakwatch:latest
script:
- leakwatch scan git . --since-commit HEAD~1 --min-severity high --format json --output results.json
artifacts:
paths:
- results.json
when: always
allow_failure: falsepipeline {
agent any
stages {
stage('Secret Scan') {
steps {
sh '''
docker run --rm \
-v "${WORKSPACE}:/scan:ro" \
ghcr.io/hodetech/leakwatch:latest \
scan fs /scan --min-severity high --format table
'''
}
}
}
}The Leakwatch Docker image uses a multi-stage build:
| Stage | Image | Purpose |
|---|---|---|
| Builder | golang:1.25-alpine |
Compiles the Go binary with CGO_ENABLED=0 |
| Runtime | alpine:3.20 |
Minimal runtime with ca-certificates and git |
- Non-root user -- The container runs as the
leakwatchuser, not root. - Minimal attack surface -- Only
ca-certificatesandgitare installed in the runtime image. - Static binary -- Built with
CGO_ENABLED=0and stripped (-ldflags="-s -w"), resulting in a small, self-contained binary. - Working directory -- Default working directory is
/scan.
The runtime image is optimized for size. The Alpine base and stripped binary keep the total image size small (typically under 50 MB).
You can extend the Leakwatch Dockerfile for custom needs.
There is no standalone rules file. Custom rules are defined under the
custom-rules: key of a .leakwatch.yaml config file (see the
Custom Rules Guide). Bake them in by copying a config that
contains them to the home directory, which Leakwatch loads as the global config:
FROM ghcr.io/hodetech/leakwatch:latest
# .leakwatch.yaml here must include the custom rules under `custom-rules:`
COPY .leakwatch.yaml /home/leakwatch/.leakwatch.yamlLeakwatch searches for .leakwatch.yaml in the working directory and then in the
home directory (~/.leakwatch.yaml). Copy your config to the home directory so it
applies to every scan regardless of the mounted working directory:
FROM ghcr.io/hodetech/leakwatch:latest
COPY .leakwatch.yaml /home/leakwatch/.leakwatch.yamlAlternatively, point at a config explicitly with --config:
docker run --rm \
-v "$(pwd):/scan:ro" \
-v "$(pwd)/.leakwatch.yaml:/cfg/.leakwatch.yaml:ro" \
ghcr.io/hodetech/leakwatch:latest scan fs /scan --config /cfg/.leakwatch.yaml# Clone the repository
git clone https://github.com/HodeTech/Leakwatch.git
cd Leakwatch
# Build the Docker image
docker build -t leakwatch:custom .
# Run with your custom build
docker run --rm -v "$(pwd):/scan:ro" leakwatch:custom scan fs /scanflowchart TD
subgraph Host["Host Machine"]
SRC["Source\n(files, repo, credentials)"]
OUT["Output\n(JSON, SARIF, CSV)"]
end
subgraph Docker["Docker Container (alpine)"]
subgraph LW["Leakwatch Process (non-root)"]
CMD["CLI Command\n(scan fs/git/image/s3/gcs)"]
ENG["Detection Engine\n(Aho-Corasick + Regex + Entropy)"]
VER["Verification Engine\n(54 verifiers (51 packages), 85.7% coverage)"]
FMT["Output Formatter"]
end
end
subgraph External["External Services"]
REG["Container Registry\n(Docker Hub, GHCR, ECR)"]
S3["AWS S3 / GCS"]
VAPI["Verification APIs\n(STS, GitHub)"]
end
SRC -->|"Volume mount (-v)"| CMD
CMD --> ENG
ENG -->|Findings| VER
VER -->|"HTTPS"| VAPI
VER --> FMT
FMT -->|"Volume mount"| OUT
CMD -->|"Network"| REG
CMD -->|"Network"| S3
When scanning remote Git repositories, Leakwatch clones them to a temporary directory. Using tmpfs keeps this in memory for faster I/O:
docker run --rm \
--tmpfs /tmp:size=1g \
ghcr.io/hodetech/leakwatch:latest \
scan git https://github.com/org/repo.gitPrevent the container from consuming excessive resources:
docker run --rm \
--memory=512m \
--cpus=2 \
-v "$(pwd):/scan:ro" \
ghcr.io/hodetech/leakwatch:latest scan fs /scan --concurrency 2The --concurrency flag should match the CPUs allocated to the container:
docker run --rm \
--cpus=4 \
-v "$(pwd):/scan:ro" \
ghcr.io/hodetech/leakwatch:latest scan fs /scan --concurrency 4If you only need detection without verification (e.g., in a pre-commit hook), skip the verification phase:
docker run --rm \
-v "$(pwd):/scan:ro" \
ghcr.io/hodetech/leakwatch:latest scan fs /scan --no-verify| Topic | Document |
|---|---|
| Getting started with Leakwatch | Getting Started Guide |
| Secret verification details | Secret Verification Guide |
| Configuration file and options | Configuration Guide |
| Architecture overview | Architecture Document |