Readiness
Readiness is optional and configured per service with the ready object.
Shape
{
"services": {
"api": {
"cmd": ["go", "run", "./cmd/api"],
"port": 3000,
"ready": {
"type": "http",
"url": "http://127.0.0.1:3000/healthz"
}
}
}
}
Supported types are none, tcp, and http.
When ready is present, ready.type is required.
No readiness
You can omit ready entirely, or set it explicitly to type: "none":
{
"services": {
"worker": {
"cmd": ["go", "run", "./cmd/worker"],
"ready": {
"type": "none"
}
}
}
}
Confirmed behavior:
- daemon service: FlowLayer does not poll; a successful spawn is enough to continue
- oneshot service: FlowLayer waits for exit code
0; there is no extra readiness probe - when
typeisnone, FlowLayer clearsready.urlandready.port
Use none when process start itself is your success condition.
TCP readiness
type: "tcp" checks whether a TCP connection can be opened.
{
"services": {
"db": {
"cmd": ["postgres", "-D", "./var/db"],
"port": 5432,
"ready": {
"type": "tcp"
}
}
}
}
Rules:
ready.portmust be greater than0- if
ready.portis omitted andservice.port > 0, FlowLayer usesservice.port - the probe connects to
127.0.0.1:<port>
TCP readiness is a good fit when "listening on the port" is the right local-ready signal.
HTTP readiness
type: "http" performs a GET request to ready.url.
{
"services": {
"api": {
"cmd": ["go", "run", "./cmd/api"],
"ready": {
"type": "http",
"url": "http://127.0.0.1:3000/healthz"
}
}
}
}
Rules:
ready.urlis required for HTTP readiness- a response with status
200to399counts as ready - probe transport errors do not fail the probe immediately; FlowLayer keeps polling
Use HTTP readiness when the process can listen before the application is actually usable.
Polling and timeout behavior
Readiness polling is fixed in the current implementation:
- FlowLayer polls every 200 ms
- each HTTP request and TCP dial uses a 200 ms attempt timeout
- there is no user-configurable per-service readiness timeout
Startup behavior depends on service type:
- daemon service: FlowLayer waits until the probe passes or the process exits
- oneshot service with readiness: FlowLayer waits for exit code
0, then keeps probing in the background until ready or session shutdown
If a daemon exits before becoming ready, startup fails instead of continuing to later waves.
Ready vs running
FlowLayer tracks process startup and readiness separately.
Practical rule:
runningmeans the process launch or start phase succeededreadymeans the readiness gate, if any, has passed
Without a readiness probe, a service can move straight from startup to ready. With a readiness probe, there can be a gap between "the process exists" and "the service is ready for dependents."
That distinction matters most when you use Waves to gate API services behind databases, migrations, or background dependencies.
Common pitfalls
ready.type: "http"withoutready.urlfails validation.ready.type: "tcp"needs eitherready.portorservice.port.- TCP readiness always probes
127.0.0.1, so it is for local listeners. - There is no built-in config knob for "fail if not ready after N seconds". A daemon that keeps running but never becomes ready will keep the wave waiting.
- The readiness schema is intentionally small. If you need custom logic, put that logic behind an HTTP endpoint or a command wrapper and keep FlowLayer on
none,tcp, orhttp.
See Waves for startup sequencing and First Stack for a minimal server config.