Some workloads require work to happen exactly once but still want to maintain multiple instances. For this, the platform exposes high availability elections.
Among all of a container's running instances, exactly one at a time is designated the primary. The platform owns the election logic and the quorum behind it, so the application never needs to embed a consensus layer or bolt on an external lock service just to coordinate.
Typical uses include a single scheduler, a queue consumer that must not double-process, a single writer in front of a shared resource, or cron-like work that should run once per container rather than once per instance.
How Elections Work
Each participating instance periodically checks in with the platform through the internal API. A check-in is both a heartbeat and a ballot: it tells Cycle the instance is alive, whether it is willing to serve as primary, and at what priority. The platform holds elections among the instances that are checking in and selects a single primary, favoring higher priorities.
Leadership is designed to be very stable. Once elected, the primary stays primary for as long as it keeps checking in. In these casess, there are no term limits and no periodic re-elections, so a healthy primary is never rotated out from under you. (Container restarts to cause a step-down event which will fire a re-election)
When an instance does stop checking in, or one that goes longer than the container's stale_primary_deadline without checking in, it is considered stale. If that instance is the primary, the platform elects a new primary from the electable secondaries, and every instance learns about the change through its next check-in response.
Enabling Elections
Elections are enabled per container through the deploy section of the container's config. The config.deploy.ha_elections object has one required field, stale_primary_deadline. In the API it looks like:
{ "deploy": { "ha_elections": { "stale_primary_deadline": "60s" } }}stale_primary_deadline is a duration string ( s, m, h, d for example, "60s") with a minimum of 15s. It sets the maximum time allowed between an instance's check-ins before the platform considers that instance stale. For the primary, it is also the upper bound on how long the container can sit without a live primary before a replacement is elected.
Sizing the Deadline
The deadline must comfortably exceed your check-in interval, or a single dropped check-in becomes a failover. See Building Reliable Primaries below for cadence guidance.
Checking In
Instances check in against the internal API, which Cycle serves inside every container instance as a unix socket at /var/run/cycle/api/api.sock. Authenticate with the X-CYCLE-TOKEN header set to the value of the CYCLE_API_TOKEN environment variable the platform injects into every instance.
curl --unix-socket /var/run/cycle/api/api.sock \ -X POST http://localhost/v1/ha/election/checkin \ -H "X-CYCLE-TOKEN: ${CYCLE_API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "electable": true, "priority": 10 }'Both request fields are required. electable declares whether this instance wants to be considered for election. priority is a 32-bit integer; when the platform elects a new primary, higher-priority instances are more likely to win.
A successful check-in returns the time the check-in was recorded and the ID of the current primary:
{ "data": { "time": "2026-07-09T17:30:00Z", "primary_instance_id": "651586fca6078e98982dbd90" }}primary_instance_id is null until a first primary has been elected. Compare it to your own instance ID, and, if it matches, this instance is the primary.
An instance that checks in with "electable": false participates without being a candidate. It stays visible to the platform and learns the current primary on every check-in, but will never be elected. Use this observer pattern for instances that need to know who the primary is in order to to route or defer work.
Check-ins require no internal API scope: the endpoint works regardless of the container's config.runtime.internal_api.scope setting.
Elections Must Be Enabled
If ha_elections is not set on the container, the check-in endpoint returns a 404 with a generic "Resource not found" error and not a 403. A surprise 404 here almost always means the container's deploy config hasn't enabled elections, not that your request is malformed.
Observing Election State
Election state is readable on the resources themselves. On the container, the ha_elections field carries the container-wide view: the most recent last_checkin, and a primary object, which is null until a primary exists, with the primary's instance_id, its priority, when it was elected, and its own last_checkin.
On each instance, the ha_elections field reports that instance's last_checkin and an elected_primary timestamp. elected_primary is only set on the instance currently holding the primary role; on every secondary it is null. This makes "who is primary right now?" answerable from either direction, the container or any individual instance.