Extending

Deployment

One container, one volume, and a readiness probe is most of what a Yatta deployment needs.

Docker

terminalbash
docker build -t yatta .docker run -p 4000:4000 \  -e NODE_ENV=production \  -e STORAGE_SECRET="$(openssl rand -hex 32)" \  yatta

The image runs as the non-root bun user, installs --production dependencies from a frozen lockfile, and ships a HEALTHCHECK pointed at /healthz.

Persist the data

SQLite files and uploaded files live on disk. Without a volume they are lost when the container is replaced.

terminalbash
docker run -p 4000:4000 \  -v yatta-data:/app/Database \  -v yatta-uploads:/app/storage/uploads \  -e STORAGE_SECRET="$(openssl rand -hex 32)" \  yatta
Note
On a single node this is sufficient. If you run more than one container, do not share a SQLite file across nodes — move storage to S3 and put a real database behind yatta/db.

Cluster mode

One process per core behind SO_REUSEPORT. SQLite is configured for exactly this — WAL, busy timeout, and schema creation under BEGIN IMMEDIATE with retries.

terminalbash
bun run cluster
or in Dockerbash
# single processdocker run -e YATTA_CLUSTER_MODE=false yatta

Orchestrator probes

kubernetesyaml
livenessProbe:  httpGet: { path: /healthz, port: 4000 }  initialDelaySeconds: 10  periodSeconds: 30 readinessProbe:  httpGet: { path: /readyz, port: 4000 }  initialDelaySeconds: 5  periodSeconds: 10 terminationGracePeriodSeconds: 20   # longer than the 10s drain
Warning
terminationGracePeriodSeconds must exceed the drain window. If the orchestrator sends SIGKILL first, in-flight work is lost and the graceful shutdown never completes.

Production checklist

  • NODE_ENV=production set
  • STORAGE_SECRET set to a real random value — startup fails otherwise
  • AUTH_SECRET set, not left at its development default
  • Database/ and storage/uploads on persistent volumes
  • Storage driver switched to s3 if you run more than one node
  • Grace period longer than the 10-second drain
  • /healthz as liveness, /readyz as readiness

What to watch

operational signalsbash
curl localhost:4000/readyz | jq '.topology, .inFlightTasks' # jobssqlite3 Database/jobs.db \  "SELECT state, COUNT(*) FROM _yatta_jobs GROUP BY state;"

A rising dead count means handlers are failing after retries. The DLQ API lists them for replay — see Jobs.