Outpost deployment guide
This guide is the single reference for all Outpost deployment modes. Choose the section that matches your environment:
- Docker Compose — development, staging, single-node
- Kubernetes / Helm — production HA deployment
- Air-gap — fully offline / disconnected environments
For a full technical component and architecture reference, see Outpost deployment architecture.
Prerequisites
Section titled “Prerequisites”- Docker Engine 24+ or Kubernetes 1.27+ with Helm 3
- Outbound HTTPS access from the host to
api.arbitex.ai(port 443) — required for policy sync, audit sync, and cert renewal - Outbound HTTPS access to your AI provider APIs (OpenAI, Anthropic, etc.) — required for request forwarding
- Admin access to the Arbitex Cloud portal
Step 1 — Register the outpost
Section titled “Step 1 — Register the outpost”Every Outpost instance must be registered in the Cloud portal before it can connect to the management plane.
- Log into the Cloud portal as an org admin.
- Navigate to Settings > Outposts > Register Outpost.
- Enter a name (e.g.,
production-vpc-us-east) and click Register. - The portal generates three values. Download them immediately — they are not shown again:
OUTPOST_ID— the UUID assigned to this outpost- Certificate bundle — a
.zipcontainingoutpost.pem,outpost.key, andca.pem
- Extract the certificate bundle to a
certs/directory on the deployment host.
Step 2 — Create the environment file
Section titled “Step 2 — Create the environment file”Create a .env file with the required configuration. Start from .env.example in the repository:
cp .env.example .envSet the required variables:
# Required — from the portal registrationOUTPOST_ID=<uuid from portal>ORG_ID=<your org UUID>PLATFORM_MANAGEMENT_URL=https://api.arbitex.ai
# Required — HMAC key for audit log integrity# Generate a strong random key:# python3 -c "import secrets; print(secrets.token_hex(32))"AUDIT_HMAC_KEY=<64-char hex string>
# mTLS certificates (from the certificate bundle)OUTPOST_CERT_PATH=certs/outpost.pemOUTPOST_KEY_PATH=certs/outpost.keyOUTPOST_CA_PATH=certs/ca.pemRecommended additional variables for production:
# Policy bundle HMAC verification (recommended)POLICY_HMAC_KEY=<64-char hex string>
# Provider key encryption (recommended if storing provider API keys in config)PROVIDER_KEY_ENCRYPTION_KEY=<Fernet key — see below>Generate a Fernet key for provider key encryption:
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"DLP configuration
Section titled “DLP configuration”| Variable | Default | Description |
|---|---|---|
DLP_ENABLED | true | Enable DLP scanning |
DLP_NER_ENABLED | true | Enable Tier 2 NER scanning |
DLP_NER_DEVICE | auto | Device selection: auto, cpu, cuda |
DLP_DEBERTA_ENABLED | false | Enable Tier 3 DeBERTa (requires model file) |
For Tier 3 DeBERTa, download the ONNX model file and set DEBERTA_MODEL_PATH to its path on the host.
Step 3 — Prepare storage directories
Section titled “Step 3 — Prepare storage directories”The Outpost writes to two persistent directories. Create them before starting the container:
mkdir -p policy_cache audit_bufferThese directories must survive container restarts. In Docker Compose they are bind-mounted as volumes. In Kubernetes they are PersistentVolumeClaims.
The audit_buffer directory holds the local HMAC-chained audit log. Do not delete it while the Outpost is running — unsynced events are stored here.
Step 4 — Deploy
Section titled “Step 4 — Deploy”Docker Compose
Section titled “Docker Compose”Use the docker-compose.outpost.yml file in the repository:
docker compose -f docker-compose.outpost.yml up -dThe Compose file configures:
- Port
8300— AI proxy (exposed to your application network) - Port
8301— Admin API (bound to127.0.0.1only) - Volume mounts for
policy_cache,audit_buffer, andcerts env_file: .envto pass your configuration
Verify the container is running:
docker compose -f docker-compose.outpost.yml psKubernetes / Helm
Section titled “Kubernetes / Helm”Add the Arbitex Helm repository:
helm repo add arbitex https://charts.arbitex.aihelm repo updateCreate a Kubernetes Secret with the mTLS certificates:
kubectl create secret generic arbitex-outpost-certs \ --from-file=outpost.pem=certs/outpost.pem \ --from-file=outpost.key=certs/outpost.key \ --from-file=ca.pem=certs/ca.pem \ -n arbitexCreate a values.yaml override file:
outpost: id: "<OUTPOST_ID>" orgId: "<ORG_ID>" platformManagementUrl: "https://api.arbitex.ai"
secrets: auditHmacKey: "<AUDIT_HMAC_KEY>" policyHmacKey: "<POLICY_HMAC_KEY>" certSecretName: "arbitex-outpost-certs"
# Resource profile — standard CPU deploymentresources: requests: memory: "512Mi" cpu: "250m" limits: memory: "2Gi" cpu: "2"
replicaCount: 2If CredInt is enabled (CREDINT_ENABLED=true, the default), the memory limit must be at least 2Gi to accommodate the ~470 MB bloom filter.
Install the chart:
helm install arbitex-outpost arbitex/arbitex-outpost \ -n arbitex \ --create-namespace \ -f values.yamlStep 5 — Verify the deployment
Section titled “Step 5 — Verify the deployment”Health check
Section titled “Health check”# Liveness — process is runningcurl http://localhost:8300/healthz
# Readiness — policy bundle is loadedcurl http://localhost:8300/readyzThe readiness endpoint returns 200 only after the Outpost has loaded its first policy bundle. This may take up to 30 seconds on first start while the initial sync completes.
If /readyz returns 503 after 60 seconds, check the container logs:
docker compose -f docker-compose.outpost.yml logs --tail=50 outpostLook for:
"Connected to management plane"— mTLS handshake succeeded"Policy bundle loaded"— first sync complete- Any
ERRORorCRITICALlines
First policy sync
Section titled “First policy sync”The Outpost polls the management plane every 60 seconds for policy bundle updates. On the first start, it fetches the bundle immediately. Verify in the Cloud portal:
- Navigate to Settings > Outposts.
- Find your outpost. The Last seen column should show a recent timestamp.
- The Policy version column shows the bundle version currently loaded.
If the outpost does not appear as active within 2 minutes, check:
OUTPOST_IDandORG_IDmatch the values from the portal registration- The host can reach
api.arbitex.aion port 443 (curl -v https://api.arbitex.ai/healthz) - The mTLS certificates are valid and match the
OUTPOST_IDregistered in the portal
Send a test request
Section titled “Send a test request”With the Outpost running, send a test request through the proxy:
curl -s -X POST http://localhost:8300/v1/chat/completions \ -H "Authorization: Bearer <your-api-key>" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "Say hello"}] }'A successful response confirms:
- The proxy is running and accepting requests
- Provider credentials are configured
- The DLP pipeline is not blocking the request
Network configuration
Section titled “Network configuration”Required firewall rules
Section titled “Required firewall rules”| Direction | Protocol | Destination | Port | Purpose |
|---|---|---|---|---|
| Inbound | TCP | Application network | 8300 | AI proxy |
| Outbound | HTTPS | api.arbitex.ai | 443 | Management plane (policy sync, audit sync, heartbeat, cert renewal) |
| Outbound | HTTPS | Provider API endpoints | 443 | Request forwarding |
Port 8301 (admin API) is bound to 127.0.0.1 — it is not accessible from the network and requires kubectl port-forward or ssh tunneling for remote access.
Proxy and egress
Section titled “Proxy and egress”If your environment routes outbound traffic through a proxy:
HTTPS_PROXY=https://proxy.internal:3128NO_PROXY=localhost,127.0.0.1Set NO_PROXY to exclude local addresses so the admin API health checks are not routed through the proxy.
Certificate management
Section titled “Certificate management”Initial certificates
Section titled “Initial certificates”Certificates are issued by the Cloud portal during registration. They are valid for 90 days. The Outpost’s CertRotationClient begins renewal automatically when the certificate is within 30 days of expiry.
Rotation
Section titled “Rotation”Rotation is fully automatic. The CertRotationClient runs hourly:
- Checks the certificate expiry date
- If within 30 days, POSTs a renewal request to the management plane
- Writes the new certificate to staging paths (
.newsuffix) - Verifies the staged certificate before swapping
- Swaps atomically (
os.replace) — no downtime or restart required
If rotation fails (network error, management plane unreachable), it retries on the next hourly cycle. The existing certificate remains in use until a successful rotation completes.
Manual certificate replacement
Section titled “Manual certificate replacement”If you need to replace the certificate manually (e.g., after a key compromise):
- Issue a new certificate bundle from the Cloud portal (Settings > Outposts > [Outpost] > Reissue Certificate).
- Copy the new
outpost.pemandoutpost.keyto thecerts/directory. - The Outpost detects the new certificate on the next hourly check and validates it. No restart is required.
Upgrading
Section titled “Upgrading”To upgrade to a new Outpost version:
Docker Compose
Section titled “Docker Compose”# Pull the new imagedocker compose -f docker-compose.outpost.yml pull
# Restart with the new imagedocker compose -f docker-compose.outpost.yml up -dDocker Compose replaces the running container. The policy_cache and audit_buffer volumes are preserved across the upgrade.
Kubernetes / Helm
Section titled “Kubernetes / Helm”helm upgrade arbitex-outpost arbitex/arbitex-outpost \ -n arbitex \ -f values.yaml \ --set image.tag=<new-version>The Helm chart is configured with a rolling update strategy and PodDisruptionBudget: minAvailable: 1 — at least one replica remains available during the upgrade.
Air-gap deployment
Section titled “Air-gap deployment”The air-gap bundle is a self-contained tarball for environments with no outbound internet access. It includes the Docker image, install script, default policy bundle, and all required dependencies.
1. Obtain the bundle
Section titled “1. Obtain the bundle”Download the air-gap bundle from Cloud Console → Outposts → [Outpost] → Download air-gap bundle, or request it from your Customer Success contact. The bundle is named:
arbitex-outpost-airgap-<version>.tar.gzA companion SHA256 checksum file is provided alongside the tarball.
2. Verify integrity before transferring
Section titled “2. Verify integrity before transferring”On a connected machine, verify the bundle before transferring to the air-gapped host:
sha256sum -c arbitex-outpost-airgap-<version>.tar.gz.sha256# arbitex-outpost-airgap-<version>.tar.gz: OK3. Transfer to the target host
Section titled “3. Transfer to the target host”Use a secure transfer method appropriate for your environment — USB drive, secure file transfer appliance, or approved courier media. Do not transfer via untrusted networks.
4. Extract and run the installer
Section titled “4. Extract and run the installer”tar -xzf arbitex-outpost-airgap-<version>.tar.gzcd airgap-<version>/bash install.shThe installer performs these steps automatically:
- Validates SHA256 checksums on the included image tarball
- Loads the Docker image into the local daemon with
docker load - Prompts for outpost ID, platform URL, and HMAC keys
- Writes a
.envfile with your configuration - Optionally installs a systemd service for automatic startup on host reboot
- Starts the outpost and verifies the health check
Prerequisites the installer checks:
- Docker Engine 24+ with Compose V2
sha256sumutility- Sufficient disk space (≥ 4 GB for the image)
To run the installer non-interactively (for automated provisioning), pre-set the required variables:
OUTPOST_ID="<uuid-from-cert-bundle>" \PLATFORM_MANAGEMENT_URL="https://management-plane.internal" \AUDIT_HMAC_KEY="$(openssl rand -hex 32)" \POLICY_HMAC_KEY="$(openssl rand -hex 32)" \bash install.sh5. systemd service
Section titled “5. systemd service”If you accepted the systemd service option during install, the outpost is managed as arbitex-outpost.service:
systemctl status arbitex-outpostsystemctl restart arbitex-outpostjournalctl -u arbitex-outpost -fTo install the systemd service manually after the fact:
# In the extracted airgap directorybash install.sh --systemd-only6. Air-gap GeoIP
Section titled “6. Air-gap GeoIP”The container image includes a minimal fallback GeoIP database. For accurate IP geolocation enrichment, mount a current MaxMind GeoLite2-City.mmdb:
# In .envGEOIP_MMDB_PATH=/app/geoip/GeoLite2-City.mmdbGEOIP_DOWNLOAD_ON_START=false # prevent startup download attemptsMount the file:
# In docker-compose.outpost.yml overridevolumes: - /path/to/GeoLite2-City.mmdb:/app/geoip/GeoLite2-City.mmdb:ro7. Air-gap policy sync
Section titled “7. Air-gap policy sync”In fully disconnected environments, the management plane sync is unavailable. The outpost will:
- Serve the last cached policy bundle from the
policy_cache/volume - Continue enforcing the cached policy indefinitely (no automatic expiry in air-gap mode)
- Log sync failures at the
warninglevel but continue operating
To push a new policy bundle manually via the admin API:
# Push a signed bundle from the air-gap packagecurl -X POST http://localhost:8301/admin/api/policy/push \ -H "X-API-Key: <OUTPOST_API_KEY>" \ -H "Content-Type: application/json" \ -d @default-policy-bundle.jsonThe default-policy-bundle.json included in the air-gap package is signed with the Platform’s signing key. Set POLICY_BUNDLE_VERIFY=false only if you are loading unsigned bundles during initial air-gap testing.
8. Air-gap Helm deployment
Section titled “8. Air-gap Helm deployment”For Kubernetes air-gap deployments, load the image into your private registry:
# Load the imagedocker load < outpost-image-<version>.tar.gz
# Tag for your registrydocker tag arbitex/outpost:<version> registry.internal/arbitex/outpost:<version>
# Push to internal registrydocker push registry.internal/arbitex/outpost:<version>Then deploy with the Helm chart, pointing to your internal registry:
image: repository: registry.internal/arbitex/outpost tag: "<version>" pullPolicy: IfNotPresent
imagePullSecrets: - name: registry-credentialsConfiguration reference
Section titled “Configuration reference”All configuration is via environment variables. The outpost validates required values at startup and exits with a descriptive error if any required variable is missing or empty.
Identity and connectivity
Section titled “Identity and connectivity”| Variable | Required | Default | Description |
|---|---|---|---|
OUTPOST_ID | yes | — | UUID from the cert bundle, issued by the Cloud portal |
ORG_ID | yes | — | Organisation UUID (from Settings → General) |
PLATFORM_MANAGEMENT_URL | yes | — | Management plane URL (e.g. https://api.arbitex.ai) |
mTLS certificates
Section titled “mTLS certificates”| Variable | Default | Description |
|---|---|---|
OUTPOST_CERT_PATH | certs/outpost.pem | Client certificate path |
OUTPOST_KEY_PATH | certs/outpost.key | Private key path |
OUTPOST_CA_PATH | certs/ca.pem | Platform CA certificate path |
Security keys
Section titled “Security keys”| Variable | Required | Description |
|---|---|---|
AUDIT_HMAC_KEY | yes | HMAC-SHA256 key for audit log chain integrity. Generate: openssl rand -hex 32 |
POLICY_HMAC_KEY | yes* | Key for policy bundle HMAC verification. *Optional when INSECURE_SKIP_HMAC=true |
OUTPOST_API_KEY | recommended | Admin interface authentication key |
PROVIDER_KEY_ENCRYPTION_KEY | recommended | Fernet key for encrypting stored provider API keys. Generate: python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" |
DLP pipeline
Section titled “DLP pipeline”| Variable | Default | Description |
|---|---|---|
DLP_ENABLED | true | Enable the full DLP pipeline |
DLP_NER_ENABLED | true | Enable Tier 2 NER (Presidio/spaCy) |
DLP_NER_MODEL | en_core_web_sm | spaCy model name |
DLP_NER_DEVICE | auto | auto, cpu, or cuda |
DLP_DEBERTA_ENABLED | false | Enable Tier 3 DeBERTa contextual detection |
DEBERTA_MODEL_PATH | — | Path to DeBERTa ONNX model file (model.onnx) |
Audit and sync
Section titled “Audit and sync”| Variable | Default | Description |
|---|---|---|
AUDIT_SYNC_INTERVAL_SECONDS | 30 | Interval between audit sync attempts to Platform |
MAX_AUDIT_BUFFER_ENTRIES | 100000 | Ring buffer cap — oldest entries pruned when exceeded |
POLICY_SYNC_INTERVAL | 60 | Policy re-sync interval in seconds |
GeoIP enrichment
Section titled “GeoIP enrichment”| Variable | Default | Description |
|---|---|---|
GEOIP_MMDB_PATH | — | Path to MaxMind GeoIP2 City MMDB |
GEOIP_MMDB_FALLBACK_PATH | /app/geoip/GeoLite2-City.mmdb | Bundled fallback DB path (baked into image) |
GEOIP_DOWNLOAD_ON_START | false | Download MaxMind DB at startup if not present |
GEOIP_DOWNLOAD_URL | — | URL for MaxMind DB download (requires license key in URL) |
GEOIP_ANON_DB_PATH | — | Path to MaxMind Anonymous IP DB |
Budget enforcement
Section titled “Budget enforcement”| Variable | Default | Description |
|---|---|---|
BUDGET_ENFORCEMENT_ENABLED | true | Enable local budget cap enforcement |
SIEM direct sink
Section titled “SIEM direct sink”| Variable | Default | Description |
|---|---|---|
SIEM_DIRECT_ENABLED | false | Enable direct push to SIEM (Splunk/Sentinel/Elastic) |
SIEM_DIRECT_TYPE | splunk_hec | splunk_hec, azure_sentinel, or elastic |
SIEM_DIRECT_URL | — | SIEM endpoint URL |
SIEM_DIRECT_TOKEN | — | SIEM authentication token |
SIEM_DIRECT_BUFFER_CAPACITY | 10000 | Event buffer capacity before flush |
SIEM_DIRECT_DEAD_LETTER_PATH | — | Path for dead-letter queue storage |
OAuth scope enforcement
Section titled “OAuth scope enforcement”| Variable | Default | Description |
|---|---|---|
OAUTH_SCOPE_ENFORCEMENT | false | Enable path-based OAuth scope enforcement (opt-in) |
OAUTH_JWT_PUBLIC_KEY | — | PEM public key for JWT signature verification |
OAUTH_JWKS_URL | — | JWKS endpoint URL (alternative to static key) |
OAUTH_JWKS_CACHE_TTL | 3600 | JWKS cache TTL in seconds |
PROMPT hold timeout
Section titled “PROMPT hold timeout”| Variable | Default | Description |
|---|---|---|
PROMPT_HOLD_TIMEOUT_SECONDS | 300 | Seconds a PROMPT-action request is held pending admin decision |
Proxy and admin
Section titled “Proxy and admin”| Variable | Default | Description |
|---|---|---|
MAX_REQUEST_BODY_MB | 10 | Maximum request body size in MB |
INSECURE_ALLOW_HTTP | false | Allow HTTP upstream connections — dev only |
Dev/test only (never use in production)
Section titled “Dev/test only (never use in production)”| Variable | Default | Description |
|---|---|---|
INSECURE_SKIP_HMAC | false | Skip HMAC key requirement at startup |
POLICY_BUNDLE_VERIFY | true | Verify policy bundle HMAC signatures |
Troubleshooting
Section titled “Troubleshooting”Outpost not connecting to management plane
Section titled “Outpost not connecting to management plane”Symptoms: /readyz returns 503, Cloud portal shows outpost as offline, logs show connection errors.
Check:
- Outbound connectivity:
curl -v https://api.arbitex.ai/healthz - Certificate validity:
openssl verify -CAfile certs/ca.pem certs/outpost.pem OUTPOST_IDmatches the UUID from the portal registration exactly
Audit log not syncing
Section titled “Audit log not syncing”Symptoms: pending_audit_events in the heartbeat payload is growing, Cloud portal shows audit events delayed.
Check:
- Management plane connectivity (same as above)
audit_buffer/directory is writable by the containerAUDIT_HMAC_KEYis set and non-empty
DLP scanning not working
Section titled “DLP scanning not working”Symptoms: Requests with PII content are not being blocked or redacted as expected.
Check:
DLP_ENABLED=true(default)- Policy bundle is loaded (check
/readyz) - The relevant DLP rules are active in the Cloud portal (Admin > DLP Rules)
- Test the DLP pipeline directly using the DLP rule testing tools
See also
Section titled “See also”- Outpost deployment architecture — complete component reference, environment variables, storage layout
- Air-gap deployment guide — offline tarball installation
- Air-gap operations guide — systemd management, offline updates, audit extraction
- Credential Intelligence — Outpost — CredInt bloom filter configuration
- Outpost SIEM direct sink — direct SIEM forwarding
- PROMPT governance — admin approval workflow for PROMPT-action requests