camelAI Documentation

Deploy

Install with Docker Compose

Install camelAI on a single x86_64 Linux VM and verify builds, notebooks, and database queries.

This guide installs camelAI on an existing Linux VM. The default configuration uses bundled Pomerium, automatic TLS, local persistent volumes, and attached Docker containers for project builds, notebooks, and database queries.

Before you start

Complete the requirements in Self-host camelAI. In particular, install Docker Engine, Docker Compose v2, Git, Node.js 22, and Bun 1.3.14.

Prepare these values:

ValueExample
camelAI hostnamecamel.example.com
Pomerium authenticate hostnameauthenticate.example.com
Deployed-app wildcard domainapps.example.com
OIDC issuer URLhttps://idp.example.com/application/o/camelai/
OIDC client ID and secretValues from your identity provider
AI provider API keyA supported provider key
DNS validation credentialCloudflare API token or Route 53 access

Register this callback URL in your OIDC application:

https://authenticate.example.com/oauth2/callback

See Authentication and Pomerium for TLS, identity-provider, and external-proxy options.

Choose a release

Production installations should use a selfhost-v* release and all seven digest-pinned camelAI images from that release's selfhost-release.json manifest. The manifest also pins the Pomerium image tested with the release. Do not mix images from different releases.

If the camelAI tags page does not contain a selfhost-v* tag, only the source-build evaluation path below is available. Do not treat a build from the moving main branch as an immutable production release.

Download selfhost-release.json from the matching camelAI GitHub Release. Release assets are permanent and public; do not depend on the expiring Actions artifact. For example:

bash
RELEASE=selfhost-vX.Y.Z
curl --fail --location \
  --output /secure/path/selfhost-release.json \
  "https://github.com/qaml-ai/camelAI/releases/download/${RELEASE}/selfhost-release.json"

For a release install, check out the matching tag:

bash
git clone https://github.com/qaml-ai/camelAI.git
cd camelAI
git checkout --detach selfhost-vX.Y.Z
bun install --frozen-lockfile
bun run selfhost:init

The camelAI release images are public and can be pulled from GHCR without a GitHub account or package token.

Set SELFHOST_DEPLOYMENT_MODE=release in .env.selfhost, then copy all eight digest-pinned image references from the manifest:

dotenv
SELFHOST_DEPLOYMENT_MODE=release
SELFHOST_APP_IMAGE=ghcr.io/qaml-ai/camelai-selfhost-app@sha256:...
SELFHOST_LOCAL_ARTIFACTS_IMAGE=ghcr.io/qaml-ai/camelai-selfhost-local-artifacts@sha256:...
SELFHOST_PROJECT_BUILD_IMAGE=ghcr.io/qaml-ai/camelai-selfhost-project-build@sha256:...
SELFHOST_ANALYSIS_IMAGE=ghcr.io/qaml-ai/camelai-selfhost-analysis@sha256:...
SELFHOST_DB_QUERY_IMAGE=ghcr.io/qaml-ai/camelai-selfhost-db-query@sha256:...
SELFHOST_CONTAINER_EGRESS_IMAGE=ghcr.io/qaml-ai/camelai-selfhost-container-egress@sha256:...
SELFHOST_CADDY_IMAGE=ghcr.io/qaml-ai/camelai-selfhost-caddy@sha256:...
SELFHOST_POMERIUM_IMAGE=pomerium/pomerium@sha256:...

Keep SELFHOST_CONTAINER_EGRESS_IMAGE set to the digest from the same release manifest. Do not replace it with Cloudflare's stock cloudflare/proxy-everything image. The camelAI image includes the Docker bridge bypass required on affected hosts; the stock sidecar can leave attached runtimes failing with Container failed to start.

If you are evaluating unreleased code, clone a tested commit and keep the source mode generated by selfhost:init:

bash
git clone https://github.com/qaml-ai/camelAI.git
cd camelAI
git checkout --detach TESTED_COMMIT_SHA
bun install --frozen-lockfile
bun run selfhost:init

Source mode builds every image on the VM. It takes longer and needs more free disk space than a release install.

Configure camelAI

Edit .env.selfhost. selfhost:init generated the signing and encryption secrets already. Do not replace them with shared development values.

Configure the public domains and bundled Pomerium:

dotenv
SELFHOST_PUBLIC_BASE_URL=https://camel.example.com
SELFHOST_MAIN_HOSTNAME=camel.example.com
LOCAL_APP_VANITY_DOMAIN=apps.example.com
LOCAL_APP_IFRAME_DOMAIN=apps.example.com

SELFHOST_AUTH_MODE=bundled-pomerium
POMERIUM_AUTHENTICATE_URL=https://authenticate.example.com
POMERIUM_AUTHENTICATE_HOSTNAME=authenticate.example.com
POMERIUM_ISSUER=camel.example.com
POMERIUM_AUDIENCE=camel.example.com
POMERIUM_DEFAULT_ORG_NAME=Example Corp
POMERIUM_IDP_PROVIDER=oidc
POMERIUM_IDP_PROVIDER_URL=https://idp.example.com/application/o/camelai/
POMERIUM_IDP_CLIENT_ID=camelai
POMERIUM_IDP_CLIENT_SECRET=replace-with-your-client-secret

LOCAL_APP_VANITY_DOMAIN and LOCAL_APP_IFRAME_DOMAIN contain the app base domain without a leading *.. With the values above, a deployed app is served at https://APP_NAME.apps.example.com—not at APP_NAME.camel.example.com or directly at apps.example.com. Create the *.apps.example.com DNS record and wildcard certificate described below.

Choose one AI provider:

camelAI uses a long-term Bedrock API key through the Bedrock Mantle compatibility endpoint. EC2 instance-profile or IMDS SigV4 model authentication is not supported by this deployment.

dotenv
SELFHOST_AI_PROVIDER=bedrock
SELFHOST_AI_API_KEY=bedrock-api-key-...
SELFHOST_AI_AWS_REGION=us-east-1

SELFHOST_AI_AWS_REGION is the preferred region. If Bedrock reports that a selected model is unavailable there, camelAI automatically tries the other supported regions for that model. Authentication and authorization errors are not retried in another region.

Publish generated apps locally

Self-hosted camelAI publishes generated apps to its local worker registry and serves them through the local dispatcher on your wildcard app domain. This is automatic: do not configure CF_API_TOKEN, CF_ACCOUNT_ID, or CF_DISPATCH_NAMESPACE for app deployment. The Compose stack fixes the latter two values to the internal self-host mode, so app deployment never calls the Cloudflare Workers API.

SELFHOST_TLS_CLOUDFLARE_API_TOKEN is unrelated to app deployment. When you select Cloudflare DNS for automatic TLS, only Caddy uses that scoped token for ACME DNS validation and certificate renewal. It can be omitted when you use Route 53, provided certificates, or an existing TLS proxy.

Choose whether deployed apps can use connections

By default, an app deployed from a workspace can receive an env.CONNECTIONS service binding. The binding lets that app list and call the workspace's configured connections. Connection credentials stay inside camelAI, but data returned by a connection can enter the deployed app.

For installations where connections should remain available to the coding agent but must not be shared with deployed apps, add this to .env.selfhost:

dotenv
CONNECTIONS_BINDING_ENABLED=false

This control requires selfhost-v0.1.2 or later. Set it before the first bun run selfhost:configure and bun run selfhost:up.

When disabled, deployed-app access through CONNECTIONS fails closed. The connection catalog methods exposed through deployed-app ANALYSIS and WAREHOUSE bindings also fail closed. Connections remain available to the camelAI coding agent, js_exec, the Connections settings page, and other control-plane workflows.

For an existing installation, change the value, run bun run selfhost:configure, and restart with bun run selfhost:up. Existing apps are blocked immediately by the runtime check. Redeploy them so their saved worker configuration no longer contains the binding.

If the agent should build apps against approved internal APIs instead, add deployment-specific instructions with custom agent skills.

Configure TLS and DNS

Caddy is the Compose ingress service. It terminates TLS in automatic and provided modes, or exposes a private HTTP origin in external mode. Pomerium remains on the loopback-only 127.0.0.1:5444 upstream. Choose a certificate mode:

dotenv
SELFHOST_TLS_MODE=automatic
SELFHOST_TLS_DNS_PROVIDER=cloudflare
SELFHOST_TLS_ACME_EMAIL=platform-ops@example.com
SELFHOST_TLS_CLOUDFLARE_API_TOKEN=...

Create a Cloudflare API token with Zone Read and DNS Edit on only this DNS zone. Caddy uses it for ACME DNS validation and automatic renewal, including the deployed-app wildcard.

Create DNS records that point all three host patterns to the VM:

camel.example.com           A/AAAA  <VM address>
authenticate.example.com    A/AAAA  <VM address>
*.apps.example.com          A/AAAA  <VM address>

Validate and start

Generate the protected Pomerium configuration and check the host:

bash
bun run selfhost:configure
bun run selfhost:doctor

selfhost:doctor checks the CLI versions, Docker daemon, CPU and memory, Docker socket access, domains, AI provider, authentication settings, Compose configuration, volumes, local service state, and bidirectional sandbox storage synchronization when the images are already local. Before first startup it may report that configured images are not local yet and defer the storage smoke.

Start the stack:

bash
bun run selfhost:up

In source mode, this command builds all seven camelAI images before starting.

Verify the installation

Check readiness:

bash
curl --fail --silent http://127.0.0.1:3001/api/selfhost/health
bun run selfhost:doctor

Then execute the storage check and all three attached runtimes:

bash
bun --env-file=.env.selfhost run selfhost:container:smoke:mount
bun --env-file=.env.selfhost run selfhost:container:smoke:project
bun --env-file=.env.selfhost run selfhost:container:smoke:analysis
bun --env-file=.env.selfhost run selfhost:container:smoke:db-query

These are functional tests. They verify bidirectional local R2/container synchronization, create a real project-build container, run a real Jupyter notebook, and load the database-query drivers, then remove the smoke containers.

Do not grant sandbox containers /dev/fuse, SYS_ADMIN, or an unconfined AppArmor profile. selfhost-v0.1.4 and later use a FUSE-free synchronization path for local R2 storage.

Open https://camel.example.com and sign in through your identity provider. The wildcard app domain should not redirect to Pomerium because published public apps use a separate unauthenticated proxy route.

The installation is ready when the health endpoint returns HTTP 200, selfhost:doctor passes, all four runtime smokes pass, and an OIDC user can open the camelAI control plane.

Continue with Operations to configure backups and learn the verified upgrade process.