Workflow automation on Kubernetes that scales

Run scheduled jobs, webhooks and APIs on Kubernetes from one config. Scale with kubectl: cron coordination, health probes and pod discovery are built in.

The failure you're trying to avoid

Three replicas, one cron line. Each pod holds its own copy of the config, so each pod's scheduler sees that line and wants to run it. Without coordination, all three do.

Deploy it

One manifest: a namespace, a throwaway Postgres, the RBAC for pod discovery, a headless Service for the mesh, a config with two routes, and the Deployment at three replicas. Replace the API key and apply it.

Check it works

Every replica health-checks every other replica, itself included:

Step 1 — Say that it is a cluster

One declaration on every pod, meaning "this agent is one of several serving the same configs". The nodes then race for a durable claim on (config, interface, minute) in Postgres, and exactly one wins. The other two skip the tick.

Step 2 — Probes that don't cost you anything

/livez answers as soon as the process is up. /readyz answers once configs are loaded, and returns 503 with "starting" until then — which is what stops the Service sending traffic to a pod that has nothing to serve yet.

Step 3 — Configs from a ConfigMap

Worth knowing what Kubernetes actually mounts here, because it bites more tools than it should: a ConfigMap volume is not a directory of files. It's a symlink farm — ..data pointing at a timestamped directory, and one symlink per key.

Step 4 — Scale out

The manifest already runs three, so go further:

Step 5 — Realtime across pods

The one thing that genuinely needs wiring: a WebSocket publish arrives at whichever pod the load balancer picked, but its subscribers are spread across every pod.

Step 6 — Let a workflow see the cluster

This is the part with no equivalent elsewhere. A discover action returns the live members of a service as an array, and lookup: fans actions out across all of them:

What you get without asking

From the same config, no extra deployment: an OpenAPI document generated from the assert tests, Prometheus metrics per route, and an OpenTelemetry trace per request showing each action and how long it took. When a nightly job gets slow, the trace says whether it was your database or the API you called, before you start guessing.

The shortcut

Everything in step 6 ships as one pack — Kubernetes Service Discovery — with four routes: list pods by label with node, image and readiness, find peers by DNS with no RBAC at all, health-check every replica, and broadcast one call to the whole Deployment. Install it, apply the RoleBinding above, and the routes work against your own cluster.

Things that will bite you

sessionAffinity: ClientIP will pin you to one pod. It's a reasonable-looking default that quietly turns three replicas into one — a load test looked beautifully stable until we noticed 12 of 12 requests hit the same pod. Leave it None unless you specifically need stickiness.

Related packs