Agent Memory
Self-hosting

Air-gapped

Run Agent Memory with no outbound network access — local models, API-key authentication and a start-up guard that refuses any dependency that would leave the perimeter.

The air-gapped profile runs Agent Memory with no route to the internet: PostgreSQL, Redis, a local OpenAI-compatible model server and the Agent Memory hosts, reachable only through one ingress proxy. The source repository ships it as a reference kit in deploy/airgap/ (compose file, environment template, ingress configuration and an acceptance script).

Two layers of enforcement

  1. Configuration guard. With Deployment__AirGapped=true, the hosts refuse to start while any configured dependency would leave the network, and the error lists every offending key. At runtime a tenant cannot route around it either: a tenant's LLM override uses the platform's local endpoint, and a non-OpenAI provider override is ignored.
  2. Network. Every service sits on an internal Docker network with no route out. The only way in is the ingress proxy, the only service attached to a second network, which forwards to the API and nothing else.

What the guard refuses

ConfiguredWhy
Stripe__SecretKey, Stripe__WebhookSecretBilling calls out to Stripe.
Auth__Google__ClientIdSign-in redirects to Google.
Email__Smtp__Password, Email__WebhookSecretExternal email relay.
Ops__SubscriptionIdCloud operations reads.
Llm__Provider=anthropicCalls the Anthropic API.
Llm__Provider=openai without a local Llm__OpenAI__BaseUrlCalls the OpenAI API.
Embedding__Provider=bedrock, or openai without a local Embedding__OpenAI__BaseUrlCalls AWS or OpenAI.

Allowed: onnx and hashing embeddings, any OpenAI-compatible base URL you set (Ollama, vLLM, LM Studio, LocalAI) and an OpenTelemetry collector inside the perimeter.

Identity

There is no identity provider to federate to, so the OAuth server stays off (OAuth__Enabled=false) and API keys are the only authentication. The web console's browser sign-in is not available in this profile; agents and operators use keys.

Bring-up

Stage images and models on a connected machine

Pull and docker save the images — pgvector/pgvector:pg17, redis:7-alpine, ollama/ollama, nginx:alpine and your Agent Memory image. Pull the model(s) once into an Ollama volume (ollama pull <model>, plus an embedding model if you use Embedding__Provider=openai). Copy the tarballs and the volume across.

Load and configure on the air-gapped host

docker load the images and restore the Ollama volume. Copy env.template to .env and fill it in. The essential keys:

Deployment__AirGapped=true
IMAGE=agent-memory:1.0

PG_OWNER_USER=postgres
PG_OWNER_PASSWORD=<secret>
APP_RUNTIME_PASSWORD=<secret>
CONTROL_PLANE_PASSWORD=<secret>
ConnectionStrings__SharedDb=Host=postgres;Database=tenant_shared;Username=app_runtime;Password=${APP_RUNTIME_PASSWORD}
ConnectionStrings__SharedDbMigrations=Host=postgres;Database=tenant_shared;Username=${PG_OWNER_USER};Password=${PG_OWNER_PASSWORD}
ConnectionStrings__ControlPlaneDb=Host=postgres;Database=agentmemory;Username=control_plane;Password=${CONTROL_PLANE_PASSWORD}
ConnectionStrings__ControlPlaneDbMigrations=Host=postgres;Database=agentmemory;Username=${PG_OWNER_USER};Password=${PG_OWNER_PASSWORD}
ConnectionStrings__PlatformDb=Host=postgres;Database=agentmemory_platform;Username=app_runtime;Password=${APP_RUNTIME_PASSWORD}
ConnectionStrings__PlatformDbMigrations=Host=postgres;Database=agentmemory_platform;Username=${PG_OWNER_USER};Password=${PG_OWNER_PASSWORD}
ConnectionStrings__Redis=redis:6379

OAuth__Enabled=false
Auth__Dev__Enabled=false
DataProtection__KeyEncryptionCertificate=<base64 PKCS#12>   # a self-signed certificate is fine

Llm__Provider=openai
Llm__OpenAI__BaseUrl=http://ollama:11434/v1
Llm__OpenAI__ApiKey=local          # any non-empty value; Ollama ignores it
Llm__OpenAI__Model=llama3.1
Embedding__Provider=openai         # or onnx / hashing (no model server needed)
Embedding__OpenAI__BaseUrl=http://ollama:11434/v1
OpenAI__ApiKey=local
Embedding__TargetDimensions=1536

Otel__Enabled=false

Start the stack

docker compose -f docker-compose.airgap.yml up -d

The migrate service creates the roles and databases; the API and the worker start only after it succeeds. The ingress publishes the API on 127.0.0.1:5199.

Mint the first key

docker compose -f docker-compose.airgap.yml run --rm --no-deps api \
  --bootstrap-key --tenant-name acme --agents support-bot

This creates a tenant and a workspace and prints one key — shown once — bound to the named agents and allowed to delete, so erasure is possible with the only credential there is. Each run creates a new tenant.

Check health

curl http://127.0.0.1:5199/health/ready

Trade-offs

  • Retrieval quality follows your local model. Reranking (in assisted mode), compression and other LLM features use whatever Llm__OpenAI__Model is. Token budgets are counted with the cl100k tokenizer, which is within roughly 15% of Llama-family tokenizers.
  • No email, so no invite or notification mail, and no console sign-in.
  • The repository's acceptance.sh repeats the reference acceptance run on any Docker host (internet is needed only to stage images): it checks start-up, the guard, key minting, retrieval with a local rerank and that egress from every service is blocked.

On this page