workspace: my-platform
# Optional: wraps all task execution with a secret manager
env_runner: "dotenvx run --"
# Optional: branch naming pattern ({slug} is replaced)
branch_pattern: "feat/{slug}"
repos:
shared:
path: ./shared
type: library # "library" or "service"
depends_on: []
env_runner: "op run --" # per-repo override
tasks:
test: unit # override canonical task name
auth-service:
path: ./auth-service
type: service
depends_on: [shared]Mothership doesn't manage secrets. It delegates to your secret manager via env_runner:
| Tool | Config value |
|---|---|
| dotenvx | dotenvx run -- |
| Doppler | doppler run -- |
| 1Password CLI | op run -- |
| Infisical | infisical run -- |
| None | omit env_runner |
For monorepos where multiple services share one git repo, use git_root to declare subdirectory services:
repos:
backend:
path: .
type: service
web:
path: web # relative — interpreted against backend's worktree
type: service
git_root: backend
depends_on: [backend]Rules:
git_rootmust reference another repo in the workspace.- The referenced repo cannot itself have
git_rootset (no chaining). - The subdirectory must exist and contain a
Taskfile.yml. - Subdirectory services still have their own
depends_on,tags,tasks, andstart_mode.
For long-running services, set start_mode: background:
repos:
infra:
path: ./infra
type: service
start_mode: background # mship run launches and moves on
backend:
path: ./backend
type: service
start_mode: background
depends_on: [infra]With start_mode: background, mship run launches the service and continues to the next dependency tier without waiting for exit. Background services keep running until Ctrl-C propagates SIGINT through go-task to their child processes. start_mode only affects mship run. Tests and logs always run foreground.
For services that need time to become ready, declare a healthcheck. mship run waits for the healthcheck to pass before starting dependent services.
repos:
infra:
path: ./infra
type: service
start_mode: background
healthcheck:
tcp: "127.0.0.1:8001" # wait for port to accept connections
timeout: 30s # optional, default 30s
retry_interval: 500ms # optional, default 500ms
backend:
path: ./backend
type: service
start_mode: background
depends_on: [infra]
healthcheck:
http: "http://localhost:8000/health"
web:
path: ./web
type: service
start_mode: background
depends_on: [backend]
healthcheck:
sleep: 3s # unconditional wait
custom:
path: ./custom
type: service
start_mode: background
healthcheck:
task: wait-for-custom # runs `task wait-for-custom`; 0 exit = readyProbe types: tcp, http, sleep, task (any one per healthcheck). Exactly one probe per healthcheck. If the probe doesn't succeed within timeout, the service is treated as failed and mship run exits non-zero. Healthchecks apply to mship run only.
If your Taskfile uses different task names than mothership's defaults (test, run, lint, setup), add a tasks: mapping:
repos:
my-app:
path: .
type: service
tasks:
run: dev # mship run → task dev
test: test:all # mship test → task test:all
lint: lint:all
setup: infra:startmship doctor respects the mapping when checking for standard tasks.
Each repo needs a Taskfile.yml with standard task names. Mothership calls task <name> in each repo. Override names per repo in the tasks mapping. Default tasks: test, run, lint, logs, setup. Missing tasks are skipped gracefully.