Skip to content

Installation

This page covers the step-by-step deployment of a self-hosted XTM One instance using Docker Compose. Before starting, read the Overview to understand the architecture and the infrastructure requirements.

Get the deployment files

Check Releases for the latest version tag.

Option A — clone a release (builds images from source):

git clone --branch <version-tag> --depth 1 https://github.com/XTM-One-Platform/xtm-one.git && cd xtm-one

Option B — use pre-built images (no build required):

VERSION="<version-tag>"

mkdir xtm-one && cd xtm-one
curl -fSLO "https://raw.githubusercontent.com/XTM-One-Platform/xtm-one/${VERSION}/docker-compose.yml"
curl -fSLO "https://raw.githubusercontent.com/XTM-One-Platform/xtm-one/${VERSION}/.env.sample"

Then create an override to pull images instead of building:

# docker-compose.override.yml
services:
  platform:
    image: xtmone/platform:<version-tag>
    build: !reset null
  worker:
    image: xtmone/worker:<version-tag>
    build: !reset null

Configure environment

cp .env.sample .env
chmod 600 .env
touch .gitignore
grep -qxF ".env" .gitignore || echo ".env" >> .gitignore

Edit .env and fill in the required values. At minimum you must set:

  • SECRET_KEY — generate with python -c "import secrets; print(secrets.token_urlsafe(48))"
  • ADMIN_EMAIL and ADMIN_PASSWORD
  • BASE_URL and FRONTEND_URL — the public URL of your instance
  • DB_PASSWORD — password for the bundled PostgreSQL
  • S3_ACCESS_KEY and S3_SECRET_KEY — credentials for the bundled MinIO

The complete list of available parameters is documented in the Configuration section.

Start

docker compose up -d

The platform waits for PostgreSQL, Redis, and MinIO health checks to pass, then automatically runs migrations, creates the admin user, and seeds default templates.

Verify

docker compose ps          # all services should show "healthy"
curl localhost:4000/api/health/ready   # 200 = fully ready

Configure the platform

  1. Open your BASE_URL in a browser.
  2. Sign in with ADMIN_EMAIL / ADMIN_PASSWORD.
  3. Go to SettingsAI Models — add at least one LLM provider.
  4. Configure authentication if needed — see Authentication.

Network requirements

Inbound

Port Purpose
4000/tcp Platform HTTP + WebSocket

Reverse proxy

The platform serves plain HTTP. For production, terminate TLS with any reverse proxy (Nginx, Caddy, Traefik, cloud load balancer) and forward to port 4000. Your proxy configuration must handle:

Requirement Why Nginx example
Forward client IP Needed for rate limiting and audit logs proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Forward protocol OAuth callbacks need the real scheme proxy_set_header X-Forwarded-Proto $scheme;
Allow large request bodies Knowledge base uploads can reach 50 MB client_max_body_size 50m;
WebSocket upgrade Required for chat streaming proxy_set_header Upgrade $http_upgrade;
Generous read timeout LLM calls can take 60s+ proxy_read_timeout 300s;

Internal (between containers)

From To Port
Platform, Worker PostgreSQL 5432
Platform, Worker Redis 6379
Platform, Worker MinIO/S3 9000

Air-gapped deployments

Air-gapped deployments work if you use a local AI provider (Ollama or any OpenAI-compatible endpoint on your network) and the built-in MinIO for storage. To get Docker images into an air-gapped network:

# On a machine with internet access — export the images (pre-built images setup from Option B):
docker compose pull
docker save xtmone/platform:<version-tag> xtmone/worker:<version-tag> pgvector/pgvector:pg17 redis:8-alpine minio/minio:latest > xtm-one-images.tar

# Transfer xtm-one-images.tar to the air-gapped host, then load:
docker load < xtm-one-images.tar

Health checks

Endpoint Use for Returns 200 when
GET /api/health Liveness probe HTTP server is running
GET /api/health/ready Readiness probe Database connected, migrations and seeding complete

The Docker Compose healthcheck uses /api/health/ready so dependent services wait for full startup.

Scaling workers

The default docker-compose.yml sets container_name: worker, which limits you to one instance. To run multiple workers:

  1. Remove (or comment out) the container_name: worker line.
  2. Scale:
docker compose up -d --scale worker=3

Each worker creates its own connection pools. Recalculate the PostgreSQL connection budget accordingly.

Next step

Configure the platform parameters in the Configuration section, and read the Upgrade procedure before your first version bump.