Running Titan services with Docker (dev)
Running Titan services with Docker (dev)
Inventory, env-file conventions, and from-scratch instructions. Written 2026-07
after auditing the running containers against the repo; the compose files were
recovered from commit 09ce283fb8 and now fully define every service.
Service inventory
| Stack | Compose file | Services (host ports) |
|---|---|---|
| titan core | docker-compose.yaml |
rails :3000, webpack :3035, sidekiq, postgres :5432, redis :6379, mailhog :1025/:8025, billing :4242 |
| bot | svc/bot/docker-compose.yml |
bot :8000, pgvector :5434, langfuse :3030, langfuse-db |
| typebot | svc/typebot/docker-compose.dev.yml |
main :8082–8084, typebot-db :5433, minio :9000/:9001, createbuckets |
| notea | svc/notea/docker-compose.yml |
notea :3001, minio :9002 |
Images are built from docker/dockerfiles/*.Dockerfile with context: svc/<name>
(same pattern as .github/workflows/ci.yaml), except typebot main
(svc/typebot/typebot.Dockerfile) and notea (docker/dockerfiles/notea.dev.Dockerfile).
Env files
Each stack reads .env in its own directory (gitignored). Two supporting file
kinds live next to it:
-
.env.example.docker(committed) — full key set with Docker-network hostnames filled in and secrets blanked (# FILLmarkers). Start here:cp .env.example.docker .env # then fill every line marked "# FILL" -
.env.docker(gitignored — contains real secrets) — faithful export of the running container’s env, taken 2026-07 viadocker inspectwith image-baked vars filtered out. Use as the reference for a working setup. Backups of the pre-audit files are kept as.env.bak.*.
Both exist for: repo root (rails/sidekiq/webpack), svc/bot/, svc/billing/,
svc/typebot/, svc/notea/.
Rules:
- One
KEY=VALUEper line. No line continuations, noexport, no CRLF. - Hostnames resolve by container name across the shared
titan_network:titan_rails_1,titan_postgres_1,titan_redis_1,bot_bot_1,typebot-main-1. Keepcontainer_name:values in compose files stable or these references break. - Secrets come from the team vault or your
.env.bak.*/.env.dockerfiles.
Known quirks:
- typebot:
DATABASE_URLandNEXT_PUBLIC_*_BASEPATHare baked into thetypebot-mainimage (DockerfileENV); keep them in.envanyway for rebuilds. - typebot’s
BOT_OUTGOING_URLmust usebot_bot_1(the oldtitan-bothostname resolves nowhere — fixed in the env files 2026-07, but the long-running container still has the broken value until recreated). - billing’s
REDIS_HOSTmust betitan_redis_1(underscores). The dash spelling in the old container never resolved — likely why billing sat exited.
Networks
| Network | Declared in | Purpose |
|---|---|---|
titan_network |
root compose (owner); external in bot/notea/typebot | Shared cross-service DNS by container name |
bot_network |
svc/bot (owner) |
bot ↔ pgvector ↔ langfuse |
typebot_network |
svc/typebot (owner) |
main ↔ typebot-db ↔ minio |
notea_internal |
svc/notea (owner) |
notea ↔ its minio |
Start the titan core stack first (it creates titan_network), or run
docker network create titan_network manually.
Leftover networks on the long-running containers are redundant and can be
cleaned up: bot_bot_network, bot_default, typebot_default, titan_default
(dead — members already share titan_network), plus rails/sidekiq memberships
in bot_network/typebot_network and bot’s membership in typebot_network.
Use docker network disconnect <net> <container>, then docker network rm.
Running from scratch
One-time:
cp .env.example.docker .env # in root and each svc dir; fill FILL lines
Boot order:
- titan core (repo root):
docker-compose up -d postgres redis mailhog, thenrails(its entrypoint runs db prepare/migrations), thensidekiq,webpack,billing. - bot (
svc/bot): needs pgvector (:5434) and rails reachable;docker-compose up -d. - typebot (
svc/typebot):docker-compose -f docker-compose.dev.yml up -d— db (:5433) + minio first;createbucketsseeds the bucket; thenmain. - notea (
svc/notea): minio (:9002) thennotea.
Gotchas:
- Port map (avoid collisions): 3000 rails · 3035 webpack · 5432 pg · 5433 typebot-db · 5434 pgvector · 6379 redis · 1025/8025 mailhog · 4242 billing · 8000 bot · 3030 langfuse · 8082–8084 typebot · 9000/9001 typebot-minio · 9002 notea-minio · 3001 notea.
REDIS_PASSWORDis read by the redis service’scommandfrom the root.env(env_file: .env) — an empty value silently starts redis unauthenticated.- postgres is pinned to postgres:14 to match the existing data volume; never point a different major version at it.
- Old containers hold the fixed
container_names:docker compose down(per project) beforeupwith recreated definitions, otherwise name conflicts. - The 2-month-old containers predate these files; recreating them picks up the
current env files (including the
BOT_OUTGOING_URL/REDIS_HOSTfixes).
Before committing / pushing the infra files
svc/notea/docker-compose.ymlhardcodesCHATWOOT_PLATFORM_TOKENin thenoteaservice environment — move it tosvc/notea/.env(value already there) and rotate the token.svc/typebot/typebot.Dockerfilecontains a hardcodedSENTRY_AUTH_TOKEN(blank + rotate) and apnpm-lock.yamletypo that silently skips the lockfile.svc/notea/.env.samplealso carries a real-lookingCHATWOOT_PLATFORM_TOKEN(committed long ago) — rotate.
Suggested additions (not yet implemented)
- Healthchecks on postgres/redis +
depends_on: condition: service_healthy. restart: unless-stoppedon app services (billing sat exited for 2 months unnoticed).- Local LangFuse (+db, port 3030) appears unused — bot points at
https://cloud.langfuse.com; consider removing the local pair. - No compose/env story for
svc/callandsvc/my(Dockerfiles exist indocker/dockerfiles/,.env.exampleexists; compose files TODO).