Deploy configuration reference
The deploy configuration lives in the source worktree’s .wos/ directory and
describes how to deploy each worktree:
.wos/deploy.yamlconfigures the source/primary/root worktree..wos/deploy.worktree.yamlconfigures every secondary worktree.
Both files live in the source worktree — secondary checkouts don’t carry their own copy. This page is a field index; the Configuration section explains each area in depth.
The mode field selects the deployment mode:
mode: generated(the default, may be omitted) — WorktreeOS generates the Docker Compose file fromapp,deps, andhost_ports. See Generated mode.mode: compose— WorktreeOS uses your existing Compose file viacompose.configand publishes only the ports incompose.expose. Theappanddepsfields are forbidden. See Compose mode.mode: shell— WorktreeOS runsapp.servicesas host shell processes instead of Docker containers. Docker-only fields (app.image, per-serviceimage/volumes,deps, package-manager cache mounts,compose) are forbidden. See Shell mode.
Top-level keys
Section titled “Top-level keys”| Key | Mode | Purpose |
|---|---|---|
mode | all | generated (default), compose, or shell. |
clone_volumes | all | Files copied from the source worktree on first run. → |
host_ports.range | all | Host-port pool (start/end, default 20000..29999). |
dynamic_ports | all | Allocate host ports dynamically (true, default) or pin each declared port to a fixed host port (false). |
cache | all | Global cache of first-run artifacts. → |
app | generated, shell | Init container (generated) or host processes (shell) + app services. |
deps | generated | External dependencies. → |
targets | generated, shell | Named service sets for --target. → |
arguments | generated, shell | Runtime argument names for --arg. → |
compose | compose | config, expose, env_file, environment. → |
app fields (generated mode)
Section titled “app fields (generated mode)”-
app.image— default image for services and the one-shot init container. -
app.init_script— first-time init commands, run once per worktree (each in its own subshell). -
app.connect_npm_cache/connect_yarn_cache/connect_bun_cache— mount host package-manager caches into the init container read-write.trueenables auto-detection; a string sets an explicit absolute or~/...path. Exposed inside the container viaNPM_CONFIG_CACHE,YARN_CACHE_FOLDER,BUN_INSTALL_CACHE_DIR.app:image: node:22connect_npm_cache: trueconnect_yarn_cache: "~/Library/Caches/Yarn/v6"connect_bun_cache: "~/.bun/install/cache"init_script:- bun install
app.services.<name> fields
Section titled “app.services.<name> fields”image— per-service image override (init container still usesapp.image).script— startup commands (joined with&&).cwd— container working directory forscript(relative resolves inside/workspace; default/workspace).init_script— service-specific first-time commands, run after the globalapp.init_script, only when the service is in the final startup set.ports— container ports to publish; number or{ port, healthcheck?, allow_failure? }. →dependencies— names of services this one depends on. →volumes— extra Docker Compose volume strings (beyond the/workspacemount).env_file— path to a.envfile (relative resolves against the worktree).environment— environment variables (coerced to strings).
deps.<name> fields (generated mode)
Section titled “deps.<name> fields (generated mode)”image, environment, volumes, and ports (numbers only; no healthcheck).
dynamic_ports
Section titled “dynamic_ports”dynamic_ports is a top-level boolean that defaults to true:
true(default) — wos allocates host ports fromhost_ports.rangeand retries on conflict, so every worktree gets its own non-clashing ports.false— wos publishes/binds each declared managed port to the same host port, ignoringhost_ports.range. Duplicate or unavailable ports fail instead of being reallocated.
dynamic_ports: false suits a fixed-port source/root worktree (for example a
shell-mode app that must bind a well-known port), while secondary worktrees keep
dynamic_ports: true so they can run side by side without port clashes.
compose fields (compose mode)
Section titled “compose fields (compose mode)”compose.config— path to the Docker Compose file (must exist atwos up).compose.expose— required, non-empty list; each entry isservice:portor{ name, port, tunnel? }.compose.env_file— env files for thedocker composeprocess (later files override earlier ones).compose.environment— inline variables overridingenv_file, with template substitution.
app fields (shell mode)
Section titled “app fields (shell mode)”Shell mode reuses the app.services shape but runs each service as a host
process. Supported keys: app.init_script and, per service,
app.services.<name>.{script, cwd, ports, env_file, environment, init_script, dependencies}. script is required. Docker-only keys (app.image,
per-service image/volumes, deps, connect_*_cache) are rejected. See
Shell mode.
Each shell service automatically receives WOS_SERVICE_PORT and
WOS_SERVICE_HOSTNAME for its first configured port (the allocated host
port and its tunnel hostname / localhost). The process must bind the allocated
host port itself.
WOS_SERVICE_PORT and WOS_SERVICE_HOSTNAME are a shared cross-mode contract:
generated Docker mode injects the same pair into each app service container with
a configured port, and compose mode injects it for each compose.expose service
through the wos-owned overlay. The pair describes only the first managed
port and always overrides user-supplied values for those keys; for additional
ports use the exact hostPort[<port>] / hostname[<port>] templates below.
Template expressions in environment
Section titled “Template expressions in environment”Templates may be embedded in plain text; references to non-existent services, ports, or undeclared arguments fail before Docker Compose starts.
Generated mode:
${app.services.<service>.containerName}${app.services.<service>.hostPort[<containerPort>]}${app.services.<service>.hostname[<port>]}— active tunnel hostname, orlocalhostwhen no tunnel is open. App services only, declared ports only.${app.services.<service>.url[<port>]}— full reachable URL (scheme, host and port): the public tunnel URL when a tunnel is open, otherwisehttp://localhost:<hostPort>. App services only, declared ports only.${deps.<service>.containerName}${deps.<service>.hostPort[<containerPort>]}${ARG}/${ARG:-default}— declared runtime arguments.
Compose mode:
${expose.<service>.hostPort[<port>]}${expose.<service>.hostname[<port>]}${expose.<service>.url[<port>]}— full reachable URL, orhttp://localhost:<hostPort>when no tunnel is open.
Shell mode:
${app.services.<service>.hostPort[<port>]}— allocated host port for a configured port.${app.services.<service>.hostname[<port>]}— active tunnel hostname for a configured port, orlocalhostwhen no tunnel is open.${app.services.<service>.url[<port>]}— full reachable URL for a configured port, orhttp://localhost:<hostPort>when no tunnel is open.${ARG}/${ARG:-default}— declared runtime arguments.
Global configuration
Section titled “Global configuration”Healthcheck timing defaults, the web UI port, and tunnel/SSL settings live in
<wos-home>/config.json, not in the deploy config. See
Daemon behavior and
Healthchecks.