Self-Hosted Deployment
Run CodeDig entirely inside your own infrastructure — Docker Compose for a single node, Helm for production Kubernetes, or raw manifests for hand-tuned clusters. The same container images power the CodeDig SaaS at codedig.ai.
Enterprise plan required. Self-hosted CodeDig is available on Enterprise contracts. Email [email protected] for image-registry credentials and pricing. The deployment artifacts referenced on this page (Helm chart, Compose file, Kubernetes manifests) are open and available in the repository today; the production-grade container images are gated behind the Enterprise agreement.
Overview
Self-hosted CodeDig is suited for teams that:
- Cannot send source code to external SaaS (compliance, ITAR, FedRAMP scope)
- Need to integrate with GitHub Enterprise Server or self-hosted GitLab
- Have data-residency requirements (EU, UK, regional Azure/AWS)
- Want to bring their own LLM (vLLM, Ollama, Azure OpenAI) for AI features
Functionally the self-hosted build is identical to the SaaS: PR-gate scoring, multi-language symbol resolution, security scanning, callgraph snapshots, and the full REST API are all bundled into the same binary. AI-assisted features call whichever LLM you point them at — there is no required CodeDig-hosted control plane.
What's included
Every deployment runs the same four runtime components:
| Component | Image | Purpose |
|---|---|---|
| api | ghcr.io/saasy-solutions/codedig | REST API, PR-gate evaluator, indexer, webhook delivery worker (all in one Rust binary). |
| web | ghcr.io/saasy-solutions/codedig-web | Next.js dashboard (findings, PR history, settings). |
| postgres | postgres:16-alpine | Primary store. All analysis results, sessions, and webhook queue state live here. |
| nats | nats:2.9 | JetStream message queue for webhook delivery. Optional but recommended. |
An optional Kubernetes Operator (crates/operator/) is shipped in the Helm chart and lets you manage CodeDig instances as custom resources for GitOps workflows. It is disabled by default in values.yaml.
System requirements
| Component | Minimum | Recommended |
|---|---|---|
| CPU (api) | 2 cores | 4 cores (8+ for large monorepos) |
| RAM (api) | 1 GiB | 2 GiB (matches helm values-prod limit) |
| CPU (web) | 250m | 500m |
| RAM (web) | 256 MiB | 512 MiB |
| Postgres | v14 | v16, 50 GiB PV, point-in-time backups |
| Disk (api) | 5 GiB | 20+ GiB for index + analysis cache |
| Architecture | linux/amd64 | linux/amd64 (arm64 builds available on request) |
| Container runtime | Docker 20 / Kubernetes 1.24 | Kubernetes 1.27+ |
Recommended values come from helm/archaeologist-operator/values-prod.yaml. Adjust upward for monorepos > 1M LOC or > 50 active engineers.
Docker Compose (single-node / evaluation)
Compose is the fastest path to a running instance and is appropriate for evaluation, dev environments, and small (under ~20-engineer) teams on a single beefy host. There is no built-in HA — for that, use Helm.
# docker/docker-compose.prod.yml (excerpt)
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:-aca}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB:-aca}
volumes:
- postgres_data:/var/lib/postgresql/data
api:
image: ${ACA_IMAGE:-ghcr.io/saasy-solutions/codedig:latest}
environment:
ACA_DB_URL: postgres://${POSTGRES_USER:-aca}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-aca}
JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET}
GITHUB_APP_ID: ${GITHUB_APP_ID:-}
# ... see docker/.env.example for the full list
ports:
- "4000:4000"
web:
image: ${ACA_WEB_IMAGE:-ghcr.io/saasy-solutions/codedig-web:latest}
ports:
- "3000:3000"Bring it up:
# 1. Copy the env template and fill in secrets
cp docker/.env.example docker/.env
$EDITOR docker/.env
# 2. Start the stack (postgres, api, web, nginx)
docker compose -f docker/docker-compose.prod.yml --env-file docker/.env up -d
# 3. Tail logs until /health returns 200
docker compose -f docker/docker-compose.prod.yml logs -f apiFull production Compose file: docker/docker-compose.prod.yml. The repo-root docker-compose.yml is the local development variant (builds from source, uses default passwords). See HA_README.md for the active-active two-node Compose topology with nginx load balancing.
Helm chart (production Kubernetes)
For multi-replica production deployments use the bundled Helm chart at helm/archaeologist-operator/. The chart ships values-dev.yaml, values-staging.yaml, and values-prod.yaml as starting points.
# Install from the chart in helm/archaeologist-operator/
helm install codedig ./helm/archaeologist-operator \
--namespace codedig --create-namespace \
-f helm/archaeologist-operator/values-prod.yaml \
--set database.host="postgres.internal" \
--set database.password="$(pass codedig/db)" \
--set llm.apiKey="$(pass codedig/openai)"
# Upgrade in place
helm upgrade codedig ./helm/archaeologist-operator \
-f helm/archaeologist-operator/values-prod.yamlThe chart provisions:
- api Deployment + HPA (2–10 replicas, CPU/memory targets) + Ingress with cert-manager annotations
- web Deployment + HPA (2–5 replicas) + Ingress
- postgresql via Bitnami subchart, 50 GiB PV with read replicas (disable and point at managed RDS / Cloud SQL by setting
postgresql.enabled=false+database.external=true) - ServiceMonitor + PrometheusRule for monitoring (requires kube-prometheus-stack)
- Pod-anti-affinity by hostname so replicas spread across nodes
See helm/archaeologist-operator/README.md for the full values reference.
Raw Kubernetes manifests
If you do not use Helm (e.g. ArgoCD with hand-curated manifests, or a custom Kustomize overlay), apply the raw YAML in kubernetes/ directly:
# Raw manifests in kubernetes/ — apply in order
kubectl apply -f kubernetes/namespace.yaml
kubectl apply -f kubernetes/configmap.yaml
kubectl apply -f kubernetes/postgres/
kubectl apply -f kubernetes/nats/
kubectl apply -f kubernetes/api/
kubectl apply -f kubernetes/web/
kubectl apply -f kubernetes/hpa.yaml
kubectl apply -f kubernetes/pdb.yaml
kubectl apply -f kubernetes/networkpolicy.yaml
# Or use the bundled kustomization
kubectl apply -k kubernetes/The raw manifests assume a StorageClass named fast-ssd for the Postgres PVC and an NGINX Ingress controller with cert-manager. Edit kubernetes/postgres/pvc.yaml and the Ingress manifests in api/ and web/ to match your cluster.
Reference: kubernetes/README.md and kubernetes/MANIFESTS.md.
Configuration
Configuration is environment-variable driven across all deployment modes. A complete template lives at docker/.env.example — copy it, fill in the required values, and pass it via --env-file (Compose) or a Kubernetes Secret mounted as env (Helm/raw).
| Variable | Required | Notes |
|---|---|---|
| ACA_DB_URL | yes | Postgres connection string. Format: postgres://user:pass@host:5432/dbname. DATABASE_URL is also accepted as a fallback. |
| JWT_SECRET | yes | Random 32+ byte string used to sign session tokens. Rotate by re-deploying with a new value (existing sessions are invalidated). |
| GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY, GITHUB_APP_CLIENT_ID, GITHUB_APP_CLIENT_SECRET, GITHUB_APP_WEBHOOK_SECRET | yes | GitHub App credentials. Required for PR analysis, webhook delivery, and OAuth login. Create the App in your GitHub org and supply all five values. |
| APP_URL | yes | Public URL of the web frontend (e.g. https://codedig.your-corp.com). Used for OAuth callbacks and webhook reply links. |
| ACA_LLM_PROVIDER, OPENAI_API_KEY, ANTHROPIC_API_KEY, OLLAMA_URL | optional | LLM configuration for AI-assisted explanations, narrative generation, and embeddings. Set OLLAMA_URL to point at a local model for fully on-prem inference. AI features can also be left disabled. |
| STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET | optional | Only needed if you intend to use the built-in billing UI. Self-hosted Enterprise deployments typically license per-seat outside Stripe and leave these unset. |
| EMAIL_PROVIDER, SENDGRID_API_KEY, EMAIL_FROM | optional | Outbound email (team invites, PR notifications). Defaults to console (logs only). Set EMAIL_PROVIDER=sendgrid and supply SENDGRID_API_KEY for real delivery. |
| NATS_URL | optional | NATS JetStream connection string for the webhook-delivery worker. Defaults to nats://nats:4222 in the bundled Compose/Helm setups. |
Repository-level analysis behavior (PR-gate thresholds, ignore patterns, required checks) is configured per-repo via .codedig.yml, not env vars.
Updates & migrations
Database schema lives in crates/storage/migrations/ and is applied by a dedicated migrate binary ( crates/migrate/). The same code path also runs at api startup, so a fresh deploy will self-migrate — but running the standalone binary as a pre-deploy step is recommended in production so that traffic only reaches replicas after the schema is at head.
# The 'migrate' binary is the deploy-time runner
# (crates/migrate/) and is the same code path the API uses
# at startup. It is idempotent — safe to re-run.
# Docker Compose: run as a one-shot before bringing api up
docker compose -f docker/docker-compose.prod.yml run --rm \
-e ACA_DB_URL="$DATABASE_URL" \
api /app/migrate
# Kubernetes: run as a Job (or pre-upgrade Helm hook)
kubectl run codedig-migrate \
--image=ghcr.io/saasy-solutions/codedig:latest \
--restart=Never --rm -it \
--env="ACA_DB_URL=$DATABASE_URL" \
--command -- /app/migrateUpgrade workflow:
- Read the changelog for the version delta you're crossing — flag any migration marked destructive or requiring backfill.
- Back up Postgres (
pg_dumpor your cloud provider's snapshot). - Pull the new image tag, run
/app/migrateas a one-shot, then roll the api/web Deployments. - Verify
GET /healthon every replica before removing the old pods.
Migrations are idempotent (each statement is wrapped and benign-error tolerant); re-running the migrate binary against an already-up-to-date schema is a no-op.
Air-gapped & data residency
Core analysis (parsing, symbol resolution, security scanning, PR-gate scoring) runs entirely inside the api container with no outbound network calls. Two surfaces optionally talk to external services; both can be disabled or pointed at internal endpoints:
- AI-assisted features (narrative explanations, embeddings, auto-documentation) call whichever LLM you configure via
ACA_LLM_PROVIDER. Set it toollama+OLLAMA_URLfor a fully on-prem inference path, or leave the API keys unset to disable AI features entirely. - GitHub / GitLab webhooks are inbound only when self-hosted — your VCS sends webhook events to your CodeDig ingress. No outbound calls back to GitHub.com are required at runtime once the GitHub App is installed against your GHES instance.
Fully air-gapped operation (no egress at all, including for container-image pulls): supported in principle — mirror ghcr.io/saasy-solutions/* into your internal registry and override the image fields in values.yaml. There is no online license-validation call at startup today, so nothing else needs to phone home. We have not yet shipped a packaged offline-installer bundle — that remains on the roadmap. Contact [email protected] if you need formal sign-off on an air-gapped reference architecture.
Support & licensing
Self-hosted CodeDig requires an Enterprise agreement. The agreement covers:
- Access to the production container image registry
- SLA-backed support (response times depend on tier)
- Security advisories and CVE patches
- Upgrade guidance for major-version transitions
To get started, email [email protected] with your team size, deployment target (cloud / on-prem / air-gapped), and any compliance requirements (SOC 2, ISO 27001, FedRAMP scope, etc.). For security questions specifically, reach [email protected].
Related: Configuration Reference · API Keys · Webhooks · Security