Each workforce member has its own
timbal.yaml manifest. The API and UI discover workforce services through env vars (TIMBAL_START_WORKFORCE, etc.) that timbal start wires up automatically.
Local development
For day-to-day work, usetimbal start. It:
- Detects
ui/,api/, and everyworkforce/<name>/with a validtimbal.yaml - Installs dependencies (
bun install,uv sync) - Picks free ports (defaults: UI
3737, API3000, workforce from4455) - Starts all services and multiplexes logs to one terminal
- Exposes interactive commands (
oopen UI,sstatus,rrestart,f/mlog focus/mute,qquit)
Environment variables
timbal start composes a separate environment for each service before spawning it. You do not need --env-file for the standard project layout — these files are auto-loaded when present:
Missing files are skipped silently.
Override or add variables at the CLI:
--env and --env-file accept an optional scope prefix: ui:, api:, or a workforce member name. Unscoped entries apply globally.
Precedence (low → high)
Later layers win over earlier ones:- Timbal built-ins (
TIMBAL_LOG_FORMAT,TIMBAL_API_KEY, etc. from your CLI profile) - Inherited shell environment (
PORTfrom your shell is ignored so it does not leak into services) - Auto-loaded
<project>/.env, then auto-loadedworkforce/<name>/.env(members only) --env-file(in flag order; scoped files apply only to that service)--env(in flag order; scoped entries apply only to that service)- Runtime wiring that always wins (
PORT,TIMBAL_START_*,TIMBAL_WORKFORCE)
r in the timbal start terminal, or quit and re-run) after changing .env files — changes are read at startup, not hot-reloaded.
.env parsing is intentionally minimal: KEY=VALUE lines, optional export prefix, optional quotes, # comments. No variable expansion, line continuations, or shell escapes. For complex values, set the variable in your shell or use --env.Syncing with the platform: timbal env pull / push
For projects connected to the Timbal Platform, timbal env keeps the local files above in sync with the project’s variables for one environment (the env tracking your current branch, --rev <branch>, or --default).
The CLI detects your current Git branch automatically. Variable types and secret recovery use the environment tracking that branch, including variables shared across all environments. Use --rev or --default only when you want to override the current branch.
Secrets vs plain: a
# type: secret (or # secret) comment directly above a var marks it as a secret, # type: plain (or # plain) as plain. A var without a comment keeps its current type on the platform; a brand-new var is inferred from its name and value (*_KEY, *_TOKEN, *_PASSWORD, sk-..., user:pass@ URLs → secret). --secret NAME / --plain NAME override everything. The platform fixes a var’s type when it is created — pushing an existing var only updates its value — so change types in the platform UI. Push refuses a file that marks a platform secret as plain unless you pass --plain NAME explicitly. The push plan always lists every var with its resolved type and where that decision came from.
TIMBAL_APP_ID remains active in each member’s local .env. On POSIX systems, new env files are created with owner-only permissions, and atomic updates preserve existing file permissions. Pull attempts to recover secrets whose values were omitted or returned as placeholders; values that remain unavailable stay commented out so a later push cannot erase them.
Production
For production you have two paths:Timbal Platform
Managed hosting — connect your git repo, deploy a branch, platform runs UI, API, and workforce for you
Self-hosted
Run UI, API, and workforce on your own infra — you own process lifecycle, ports, env wiring, and logging