Skip to content

Ships & Fleet

Ships are traditional processes that bind to a port or Unix socket. Mothership launches them, monitors them, and proxies HTTP and WebSocket traffic to them.

Ships are declared in named groups under [[fleet.GROUP]] — the group name (web, workers, jobs, …) is yours to choose and shows up in metrics and the TUI.

# Web ships
[[fleet.web]]
name = "app"
command = "ruby"
args = ["app.rb"]
cwd = "./apps/web"
bind = "tcp://127.0.0.1:3000"
healthcheck = "/health"
healthcheck_initial_delay = 10
routes = [{ bind = "http", pattern = "/.*" }]
# Background workers
[[fleet.workers]]
name = "sidekiq"
command = "bundle"
args = ["exec", "sidekiq"]
critical = false
# One-shot jobs
[[fleet.jobs]]
name = "migrate"
command = "rails"
args = ["db:migrate"]
oneshot = true
Field Type Default Description
name string required Unique identifier (ASCII only, no spaces)
command string required Command to execute
args string[] [] Command arguments
cwd string inherited Working directory for process launch
bind string Internal bind address (tcp://host:port or unix:///path), with $ENV_VAR expansion
healthcheck string Health check endpoint path
healthcheck_initial_delay u64 30 Seconds to wait before the first health check
routes array [] HTTP routes (see Routing)
depends_on string[] [] Ships/bays to wait for before starting
env table {} Environment variables
critical bool true Crash kills the entire fleet
oneshot bool false Run once and exit
tags string[] [] Tags for filtering (--only, --except)

healthcheck_initial_delay is useful for slow starters — apps that need to warm caches or establish upstream connections before /health can succeed.

bind = "tcp://127.0.0.1:3000" # TCP with explicit prefix
bind = "0.0.0.0:8080" # TCP without prefix
bind = "3000" # port only (defaults to 127.0.0.1)
bind = "unix:///tmp/app.sock" # Unix socket
bind = "0.0.0.0:$PORT" # env-expanded

On the same machine, Unix sockets skip the network stack entirely — prefer them when the backend supports it.

A critical ship (the default) that crashes takes the whole fleet down. Set critical = false for processes the fleet can live without — background workers, non-essential sidecars. Crashing ships get exponential-backoff restarts (crash-loop protection) instead of hot restart loops.

When healthcheck is set, mothership probes the ship over HTTP with retries and circuit breakers. Health state is exported in Prometheus metrics and shown in the TUI.

Ships inherit mothership’s environment; per-ship env entries override inherited values:

Terminal window
MASTER_KEY=secret DATABASE_URL=postgres://... mothership run
[[fleet.web]]
name = "app"
command = "ruby"
env = { RAILS_ENV = "production" }

When loading ship-manifest.toml, mothership also loads a .env file from the manifest directory first. Existing process environment variables are preserved and never overridden by .env.

Terminal window
mothership --only web # only ships tagged "web"
mothership --only web,api # OR logic
mothership --except workers # everything except "workers"

Reduce duplication with [base.ship] — every ship inherits its values, and list fields like tags are combined:

[base.ship]
env = { RAILS_ENV = "production" }
critical = true
healthcheck_initial_delay = 15
tags = ["ruby"]
[[fleet.web]]
name = "app"
command = "ruby"
tags = ["web"] # effective tags: ["ruby", "web"]

[base.bay] and [base.module] do the same for bays and WASM payloads.