Skip to main content
Tale runs as five Docker containers managed by Docker Compose. Each container has a single responsibility and communicates over an internal bridge network.

Service overview

Image details

ServiceBase imageOriginal sizeOptimized sizeSavingsBuild strategy
Platformghcr.io/get-convex/convex-backend~2.98 GB2.58 GB13%6-stage: deps → builder → pruner → dashboard → runner → squash
Crawlerpython:3.11-slim~2.05 GB1.85 GB10%3-stage: builder → runtime → squash. Chromium headless_shell only
RAGpython:3.11-slim~539 MB515 MB4%3-stage: builder → runtime → squash. libpq5 only
DBparadedb/paradedb:0.22.5-pg16~2.44 GB1.06 GB57%3-stage: cleanup → runtime → squash. Debug symbols + LLVM stripped
Proxycaddy:2.11-alpine~88 MB88 MBSingle stage. Already minimal
Total~10.1 GB~6.1 GB40%

Port mapping

Development ports (compose.yml)

ServiceHost portContainer portProtocol
DB54325432TCP (PostgreSQL)
Crawler80028002HTTP
RAG80018001HTTP
Platform3000, 3210HTTP (via proxy)
Proxy80, 44380, 443HTTP/HTTPS

Test ports (compose.test.yml)

ServiceHost portContainer port
DB154325432
Crawler180028002
RAG180018001
Platform13000, 132103000, 3210
Proxy10080, 1044380, 443

Volume mapping

VolumeMounted inPathPurpose
db-dataDB/var/lib/postgresql/dataPostgreSQL data directory
db-backupDB/var/lib/postgresql/backupDatabase backups
rag-dataRAG/app/dataTemp files, document processing
crawler-dataCrawler/app/dataWebsite registry, URL databases
platform-dataPlatform/app/dataConvex DB, agents, workflows
platform-dataCrawler, RAG/app/platform-configShared provider config (read-only)
caddy-dataProxy, Platform/data, /caddy-dataTLS certificates
caddy-configProxy/configCaddy configuration
Important: Never run docker compose down -v. The -v flag deletes all Docker volumes, permanently erasing your database and all platform data.

Build arguments

ArgumentDefaultUsed byDescription
VERSIONdevAllImage version tag (set by CI from git tag)
INSTALL_CJK_FONTSfalseCrawlerInstall CJK font support (~100 MB)

Multi-stage build strategy

All services use a FROM scratch squash as their final stage. This flattens Docker layers so that file deletions in cleanup stages actually reclaim disk space, rather than just adding masking layers.

Platform (6 stages)

  1. bun-bin — Extracts Bun binary
  2. workspace-deps — Installs all npm dependencies (including devDependencies)
  3. builder — Runs vite build to produce the SPA
  4. pruner — Reinstalls production-only deps, removes dev packages (@vitest, @storybook, typescript, etc.)
  5. runner — Final runtime on Convex backend base with pruned node_modules, strips LLVM/Clang (~155 MB)
  6. squashFROM scratch + COPY --from=runner. Runs as root, drops to app user via gosu in entrypoint

Crawler (3 stages)

  1. builder — Installs Python deps via uv, downloads Chromium headless_shell, runs deep cleanup (removes full Chrome, FFmpeg, pip, __pycache__, .so debug symbols, test dirs)
  2. runtime — Clean python:3.11-slim with only runtime system libs (Chromium deps, tini, curl). Strips LLVM/Adwaita icons
  3. squashFROM scratch + COPY --from=runtime. Pre-creates volume mountpoints for /app/data and /app/platform-config

RAG (3 stages)

  1. builder — Installs Python deps with build-essential + libpq-dev for compiling native packages, then strips pip/setuptools
  2. runtime — Clean python:3.11-slim with only libpq5 + curl. Pre-creates volume mountpoints
  3. squashFROM scratch + COPY --from=runtime

DB (3 stages)

  1. cleanup — Strips debug symbols (~888 MB), LLVM shared libraries (~127 MB), PostGIS extension files, locales, and docs from the ParadeDB base image
  2. runtimeFROM scratch + COPY --from=cleanup. Fresh layer with only cleaned files
  3. squash — Re-declares PGDATA, PATH, and other ENV vars lost during FROM scratch

Health checks

ServiceEndpointProtocolStart period
DBpg_isready -U tale -d taleCLI60s
CrawlerGET /health on :8002HTTP40s
RAGGET /health on :8001HTTP40s
PlatformGET /api/health + GET :3210/versionHTTP120s
ProxyGET /health on :2020 (internal)HTTP10s

Container testing

Tale includes three container test scripts:
ScriptCommandWhat it tests
tests/container-smoke-test.shbun run docker:testBuilds, starts, health checks, HTTP endpoints, inter-service connectivity
tests/container-image-test.shbun run docker:test:imageOCI labels, non-root user, no secrets, HEALTHCHECK instruction, size budgets
tests/container-vulnerability-scan.shbun run docker:test:vulnerabilityTrivy vulnerability scan (HIGH + CRITICAL)
See Contributing Docker guide for details on modifying Dockerfiles and running tests.
Last modified on April 6, 2026