Air-gap deployment guide
This guide covers deploying Arbitex Outpost on hosts with no internet access. The air-gap workflow has two phases: building the offline package on a machine that has internet access, then installing it on the air-gapped target host.
Prerequisites
Section titled “Prerequisites”Package builder (internet-connected machine)
Section titled “Package builder (internet-connected machine)”Run scripts/make-airgap.sh on a machine with:
- Docker Engine 24 or later with Compose V2 (
docker compose version) - Access to the
arbitex-outpostrepository - (Optional) A MaxMind GeoLite2-City MMDB file at
geoip/GeoLite2-City.mmdb— required for GeoIP-based routing and compliance features
Target host (air-gapped machine)
Section titled “Target host (air-gapped machine)”- Docker Engine 24 or later with Compose V2
- A user account in the
dockergroup, or root - systemd (optional — for automatic startup on boot)
- Sufficient disk space: the package tarball is approximately 5–6 GB depending on configuration
- Sufficient RAM: 2 GB minimum; 4 GB recommended for Tier 3 (DeBERTa) DLP
mTLS certificates
Section titled “mTLS certificates”Before the Outpost can connect to the management plane, you need three certificate files obtained from the Arbitex Cloud portal when you register the outpost:
| File | Purpose |
|---|---|
outpost.pem | Outpost mTLS client certificate |
outpost.key | Private key for the client certificate |
ca.pem | Arbitex CA certificate for server verification |
Register the outpost at cloud.arbitex.ai and download the certificate bundle before starting the installation.
Step 1 — Build the air-gap package
Section titled “Step 1 — Build the air-gap package”On the internet-connected build machine, from the arbitex-outpost project root:
bash scripts/make-airgap.shTo build a specific version:
bash scripts/make-airgap.sh 1.2.0If no version is provided, the script uses the current git tag if one exists on HEAD, or falls back to the current date (YYYYMMDD).
What the script builds
Section titled “What the script builds”The script runs the following steps automatically:
- Verifies Docker Engine 24+ and Compose V2 are available
- Builds the CPU Docker image:
arbitex/outpost:<VERSION> - Builds the GPU Docker image:
arbitex/outpost:<VERSION>-gpu(--build-arg INFERENCE_MODE=gpu) - Saves both images to a single compressed tarball:
dist/outpost-image-<VERSION>.tar.gz - Stages the following files into a packaging directory:
docker-compose.outpost.yml.env.exampleairgap-install.sh(renamed toinstall.shin the package)charts/arbitex-outpost/(full Helm chart, if present)- The image tarball
default-policy-bundle.json(bootstrap bundle — an empty placeholder so the outpost can start before reaching the management plane)GeoLite2-City.mmdb(if present atgeoip/GeoLite2-City.mmdb)
- Generates a
image.sha256checksum for the image tarball - Archives everything into the final package:
dist/arbitex-outpost-airgap-<VERSION>.tar.gz - Writes a SHA-256 checksum file:
dist/arbitex-outpost-airgap-<VERSION>.tar.gz.sha256
GeoIP bundling
Section titled “GeoIP bundling”If geoip/GeoLite2-City.mmdb is present when the script runs, it is included in the package. GeoIP-based routing and compliance features (geographic restriction rules, anonymous IP detection) require this file.
If the MMDB is not included, GeoIP features are disabled on the air-gapped host. GeoIP can be added later by placing the MMDB at /opt/arbitex-outpost/geoip/GeoLite2-City.mmdb and setting MAXMIND_DB_PATH=/app/geoip/GeoLite2-City.mmdb in .env, then restarting the outpost.
Output files
Section titled “Output files”| File | Description |
|---|---|
dist/arbitex-outpost-airgap-<VERSION>.tar.gz | Full air-gap package — transfer this to the target host |
dist/arbitex-outpost-airgap-<VERSION>.tar.gz.sha256 | SHA-256 checksum — transfer alongside the tarball |
Step 2 — Transfer the package
Section titled “Step 2 — Transfer the package”Transfer the tarball and checksum file to the air-gapped target host using any available method (SCP, removable media, internal file transfer):
scp dist/arbitex-outpost-airgap-<VERSION>.tar.gz \ dist/arbitex-outpost-airgap-<VERSION>.tar.gz.sha256 \ user@target-host:/tmp/Step 3 — Install on the air-gapped host
Section titled “Step 3 — Install on the air-gapped host”On the target host, verify the package integrity before proceeding:
cd /tmpsha256sum -c arbitex-outpost-airgap-<VERSION>.tar.gz.sha256Expected output: arbitex-outpost-airgap-<VERSION>.tar.gz: OK
If verification fails, the package is corrupt or was tampered with during transfer. Do not proceed.
Extract and run the installer:
tar -xzf arbitex-outpost-airgap-<VERSION>.tar.gzcd airgap-<VERSION>/bash install.shStep 4 — Installer walkthrough
Section titled “Step 4 — Installer walkthrough”The installer (install.sh) runs interactively and prompts for configuration. The steps are:
4a. Checksum validation
Section titled “4a. Checksum validation”The installer re-verifies the Docker image tarball checksum against the embedded image.sha256 file. If verification fails, installation stops.
4b. Docker image load
Section titled “4b. Docker image load”Loading Docker image...[arbitex] Loading image from: outpost-image-<VERSION>.tar.gzBoth the CPU and GPU images are loaded into the local Docker daemon from the tarball (docker load). No network access is required.
This step may take 2–5 minutes depending on disk and CPU speed.
4c. Configuration prompts
Section titled “4c. Configuration prompts”The installer prompts for the following values. All values can be pre-set as environment variables before running install.sh to enable unattended installation.
| Prompt | Environment variable | Required | Notes |
|---|---|---|---|
| Outpost ID | OUTPOST_ID | Yes | UUID from cloud.arbitex.ai/outposts |
| Platform URL | PLATFORM_MANAGEMENT_URL | No | Defaults to https://api.arbitex.ai |
| Audit HMAC key | AUDIT_HMAC_KEY | Yes | Long random string; generate with openssl rand -hex 32 |
| Emergency admin key | OUTPOST_EMERGENCY_ADMIN_KEY | No | Provides local admin access when management plane is unreachable; leave blank to disable |
| GPU mode | GPU_MODE | No | Enter y to use the GPU image; defaults to CPU |
The audit HMAC key seeds the tamper-evident chain on the local audit log. It must be retained — audit log verification requires the original key.
4d. Install directory
Section titled “4d. Install directory”Default install directory: /opt/arbitex-outpost
Override with the INSTALL_DIR environment variable before running the installer.
4e. Policy cache bootstrap
Section titled “4e. Policy cache bootstrap”The installer places the included default-policy-bundle.json at policy_cache/policy_bundle.json. This is an empty bootstrap bundle:
{ "version": "bootstrap-offline", "providers": {}, "routing_rules": {}, "dlp_rules": [], ...}On first boot, the outpost loads this bundle so it can start without network access. Once the outpost reaches the management plane (even briefly), the bootstrap bundle is replaced with the live policy bundle. Until then, the outpost routes requests using the empty bootstrap bundle — which means no DLP rules and no provider routing are active. Add the mTLS certificates before allowing user traffic.
4f. GeoIP database
Section titled “4f. GeoIP database”If GeoLite2-City.mmdb was included in the package, the installer copies it to ${INSTALL_DIR}/geoip/ and sets MAXMIND_DB_PATH=/app/geoip/GeoLite2-City.mmdb in .env.
If the MMDB is absent, a warning is printed and GeoIP features are disabled.
4g. Runtime directory creation
Section titled “4g. Runtime directory creation”The installer creates three required directories under the install path:
| Directory | Purpose |
|---|---|
certs/ | mTLS certificate files |
audit_buffer/ | Local HMAC-chained audit log |
policy_cache/ | Cached policy bundle |
These directories are created empty. The outpost will not connect to the management plane until you place certificate files in certs/.
4h. systemd service (optional)
Section titled “4h. systemd service (optional)”If systemd is running on the host, the installer offers to install a systemd service for automatic startup on boot. This requires root.
Service details:
| Property | Value |
|---|---|
| Service name | arbitex-outpost |
| Service file | /etc/systemd/system/arbitex-outpost.service |
| Working directory | ${INSTALL_DIR} |
| Start command | docker compose -f docker-compose.outpost.yml up |
| Stop command | docker compose -f docker-compose.outpost.yml down |
| Restart policy | Always, with 10-second delay |
To skip systemd installation, answer N at the prompt. The installer will start the outpost directly with docker compose up -d.
Step 5 — Place mTLS certificates
Section titled “Step 5 — Place mTLS certificates”Before the outpost can reach the management plane, place the certificate files downloaded from the cloud portal:
cp outpost.pem /opt/arbitex-outpost/certs/cp outpost.key /opt/arbitex-outpost/certs/cp ca.pem /opt/arbitex-outpost/certs/The outpost reads these paths from environment variables (defaults shown):
| Variable | Default |
|---|---|
OUTPOST_CERT_PATH | certs/outpost.pem |
OUTPOST_KEY_PATH | certs/outpost.key |
OUTPOST_CA_PATH | certs/ca.pem |
If you installed systemd, restart the service after placing the certificates:
sudo systemctl restart arbitex-outpostOtherwise restart with Docker Compose:
cd /opt/arbitex-outpostdocker compose -f docker-compose.outpost.yml restartStep 6 — Verify the installation
Section titled “Step 6 — Verify the installation”Check that the outpost is running and healthy:
# Health probe (liveness)curl http://localhost:8300/healthz
# Readiness probe (fails until policy bundle is loaded)curl http://localhost:8300/readyzBoth should return HTTP 200 once the outpost has loaded its policy bundle. If /readyz returns 503, the outpost is running but has not yet loaded a policy bundle — check that the policy cache bootstrap file is present and that the outpost can reach the management plane.
Test the AI proxy endpoint:
curl http://localhost:8300/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"ping"}]}'GPU vs CPU mode
Section titled “GPU vs CPU mode”The package includes both a CPU image and a GPU image. Mode is selected at configuration time during installation. To change mode after installation:
- Edit
/opt/arbitex-outpost/.envand update the image tag referenced indocker-compose.outpost.yml - Restart the outpost
CPU mode (default): Uses arbitex/outpost:<VERSION>. No GPU required. DeBERTa Tier 3 DLP runs on CPU — enable with DLP_DEBERTA_ENABLED=true and DEBERTA_MODEL_PATH=<path>. CPU DeBERTa inference adds 50–500 ms per request depending on text length.
GPU mode: Uses arbitex/outpost:<VERSION>-gpu. Requires:
- NVIDIA GPU with CUDA support
- NVIDIA Container Toolkit installed (
nvidia-docker2) docker-compose.outpost.ymlconfigured to pass the GPU runtime
GPU mode reduces DeBERTa inference to 10–50 ms and enables higher request throughput.
See Outpost deployment architecture for DLP resource requirements by mode.
Unattended installation
Section titled “Unattended installation”Pre-set environment variables to skip all prompts:
export OUTPOST_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"export PLATFORM_MANAGEMENT_URL="https://api.arbitex.ai"export AUDIT_HMAC_KEY="$(openssl rand -hex 32)"export OUTPOST_EMERGENCY_ADMIN_KEY="$(openssl rand -hex 24)"export GPU_MODE="N"export INSTALL_DIR="/opt/arbitex-outpost"
bash install.shSee also
Section titled “See also”- Air-gap operations guide — log rotation, systemd management, offline updates, audit extraction
- Outpost deployment architecture — component inventory, network topology, resource requirements
- Outpost SIEM direct sink — forwarding audit events directly to Splunk or syslog
- Outpost deployment guide — standard internet-connected deployment