Skip to main content
A cluster singleton is an actor with exactly one instance across the entire cluster. It is hosted on the cluster coordinator (oldest node). When that host node leaves the cluster gracefully, the singleton is recreated on the new coordinator.

Singletons vs normal actors

AspectSingletonNormal actor
InstancesExactly one per clusterOne per spawn (many per node or cluster)
PlacementCoordinator (oldest node) or roleLocal, or any node via SpawnOn
ClusterRequires cluster modeWorks standalone or in cluster
LifecycleLong-lived, relocated on shutdownExplicit spawn/stop, can passivate
APISpawnSingletonSpawn, SpawnOn, SpawnFromFunc

When to use singletons

  • Cluster-wide coordinator β€” Job scheduler, leader election, or coordination that must run once across the cluster
  • Single source of truth β€” Centralized state or registry that must be unique (e.g. license manager, cluster metadata)
  • Exclusive resource β€” A resource that must have exactly one owner (e.g. distributed lock coordinator)

When to use normal actors

  • Scalable workers β€” Many instances for parallelism (e.g. request handlers, workers)
  • Per-entity or per-request β€” One actor per user, session, or task
  • Explicit lifecycle β€” You control when to spawn and stop
  • Standalone or local-only β€” No cluster, or node-local state

Requirements

  • Cluster mode β€” Singletons require clustering. SpawnSingleton returns ErrClusterDisabled when cluster mode is off.
  • Remoting β€” Clustering requires remoting; the singleton may run on a different node than the caller.
  • Actor kind registered β€” The actor type must be registered via WithKinds when creating the actor system.

API

pid, err := system.SpawnSingleton(ctx, "scheduler", NewSchedulerActor())
Use ActorOf(ctx, name) to resolve the singleton from any node. Messaging is location-transparent: Tell and Ask work the same whether the singleton is local or remote. From inside Receive, use ctx.Request(pid, msg, opts...) for non-blocking request-response.

Placement

  • Default β€” The singleton runs on the cluster coordinator (oldest node by membership).
  • With role β€” Use WithSingletonRole(role) to pin the singleton to nodes that advertise that role. The oldest node with the role (by CreatedAt) hosts it. If no node has the role, SpawnSingleton retries according to the spawn options; it returns an error only after retries are exhausted or the spawn timeout elapses.

Configuration options

OptionPurposeDefault
WithSingletonRole(role)Pin singleton to nodes with this roleβ€”
WithSingletonSpawnTimeout(d)Max time to wait for spawn (retries + checks)30s
WithSingletonSpawnWaitInterval(d)Delay between retry attempts500ms
WithSingletonSpawnRetries(n)Max retry attempts before giving up5
Example with role:
pid, err := system.SpawnSingleton(ctx, "scheduler", NewSchedulerActor(),
    actor.WithSingletonRole("control-plane"),
    actor.WithSingletonSpawnTimeout(10*time.Second),
)

Idempotency

Calling SpawnSingleton multiple times with the same actor kind (and role, if set) typically returns ErrSingletonAlreadyExists when the singleton is already registered. Use ActorOf(ctx, name) to obtain the PID of an existing singleton.

Relocation

When the host node leaves the cluster gracefully, the relocator recreates the singleton on the new coordinator. See Relocation for the full flow. If the host node crashes (kill -9, OOM, etc.), relocation does not run; the singleton is lost. A subsequent SpawnSingleton may return ErrSingletonAlreadyExists if the cluster still has the singleton kind registered.

Lifecycle

  • A singleton is created with a OneForOne supervisor strategy and lives for the lifetime of the actor system. The supervisor configuration is fixed internally (not configurable via SpawnSingleton options).
  • It is not passivated by default; use WithPassivationStrategy if you need idle-based deactivation (uncommon for singletons).
  • Use pid.IsSingleton() to check whether a PID is a cluster singleton.

Errors

ErrorWhen
ErrClusterDisabledCluster mode is not enabled
ErrLeaderNotFoundNo coordinator (oldest node) in the cluster
ErrSingletonAlreadyExistsAnother singleton of the same kind (and role) is already registered
ErrActorAlreadyExistsName collision; retries may resolve if the existing actor is the singleton
Quorum errorsCluster cannot reach write/read quorum; retries may succeed

See also