Docker Compose is the quickest way to get a self-hosted Titan instance running on a single server or local development machine. The stack includes all six services Titan needs — API server, Runner, Media worker, PostgreSQL, Redis, and RabbitMQ — wired together and ready to start with a single command.Documentation Index
Fetch the complete documentation index at: https://docs.usetitan.app/llms.txt
Use this file to discover all available pages before exploring further.
Prerequisites
- Docker Engine 24+ and Docker Compose v2 (
docker composenotdocker-compose) - A Titan license key — contact Titan if you do not have one yet
- Outbound internet access from the server (for WhatsApp connectivity)
Setup
Download the Compose file
Download the official The file defines six services:
docker-compose.selfhost.yml file and rename it to docker-compose.yml:| Service | Image | Default port |
|---|---|---|
titan-api | Titan API server | 8080 (API), 9090 (metrics) |
titan-runner | WhatsApp session manager | — (internal only) |
titan-media | Media download and S3 upload | 8082 |
postgres | PostgreSQL 16 | 5432 |
redis | Redis 7 | 6379 |
rabbitmq | RabbitMQ 3.13 with management UI | 5672, 15672 |
Create your .env file
Copy the example file and fill in your values:Edit
.env and set the following required variables:.env
API_KEY_SECRET and MASTER_KEY are separate credentials. API_KEY_SECRET is used internally to sign user-facing API keys. MASTER_KEY is the bearer token you use to authenticate against the Admin API. Both must be set before starting the stack.Start the stack
Pull images and start all services in detached mode:Docker Compose starts the infrastructure services first and waits for health checks to pass before starting the Titan services. On a fresh machine, the first start takes a minute or two while images are pulled.Watch the logs to confirm everything is running:You should see a line similar to:
Environment variable reference
The following table covers every variable that changes runtime behavior. Variables with a default value are optional; all others are required.| Variable | Description | Default |
|---|---|---|
LICENSE_KEY | Your Titan license key | — |
MASTER_KEY | Bearer token for the Admin API | master_changeme |
API_KEY_SECRET | Secret for signing API keys. Generate with openssl rand -hex 32. | changeme_generate_a_random_secret |
POSTGRES_USER | PostgreSQL username | titan |
POSTGRES_PASSWORD | PostgreSQL password | titan |
RABBITMQ_USER | RabbitMQ username | guest |
RABBITMQ_PASSWORD | RabbitMQ password | guest |
S3_ENDPOINT | S3-compatible storage endpoint URL | — |
S3_BUCKET | Storage bucket name | — |
S3_REGION | Storage bucket region | — |
S3_ACCESS_KEY | Storage access key ID | — |
S3_SECRET_KEY | Storage secret access key | — |
MEDIA_AUTO_PERSIST | Automatically save all incoming media to S3 | false |
LOG_LEVEL | Log verbosity: debug, info, warn, error | info |
MAX_SESSIONS_PER_POD | Max concurrent WhatsApp sessions per Runner pod | 50 |
API_PORT | Host port for the API server | 8080 |
MEDIA_PORT | Host port for the media worker | 8082 |
METRICS_PORT | Host port for Prometheus metrics | 9090 |
ENV | Runtime environment (production or development) | production |
Network and security
The Compose file exposes service ports onlocalhost by default. Before putting this instance in front of real traffic:
- Place the API server behind a reverse proxy (nginx, Caddy, or a cloud load balancer) with TLS termination
- Do not expose the PostgreSQL (
5432), Redis (6379), or RabbitMQ (5672) ports to the public internet — they are for internal service communication only - The RabbitMQ management UI (
15672) should be firewalled or tunnelled rather than exposed publicly - Set
RABBITMQ_USERandRABBITMQ_PASSWORDto non-default values — Titan logs a warning at startup if the defaultguest/guestcredentials are in use
Scaling
The API server is fully stateless and can run as multiple replicas behind a load balancer. To scale the API tier:MAX_SESSIONS_PER_POD concurrent sessions (default: 50). If a Runner pod stops unexpectedly, the API detects the stale heartbeat within 15 seconds and reassigns its sessions to healthy pods.
For production workloads with more than a few hundred sessions, consider moving to Kubernetes, which adds horizontal pod autoscaling and pod disruption budgets.