discovery.Provider interface. Pass the provider to ClusterConfig.WithDiscovery() when creating the cluster.
Provider interface
Provider interface
- ID: Provider name.
- Initialize: One-time setup (e.g., connect to backend).
- Register: Register this node with the discovery backend.
- Deregister: Remove this node on shutdown.
- DiscoverPeers: Return peer addresses as
host:portstrings. Called when the node boots. See Discovery happens at boot for what an empty list and an error mean. - Close: Release resources.
Built-in providers
GoAkt ships eight providers. Each one lives in its own package underdiscovery/ and is created with that package’s
NewDiscovery function, which takes a pointer to the package’s Config. The configuration is validated when the
cluster starts, so a missing required field surfaces as a startup error.
Every provider returns peer addresses on the discovery port, the port set with WithDiscoveryPort that carries
membership gossip. Providers that register the node with a backend take the node’s Host and DiscoveryPort in their
configuration, and those values must match what the cluster listens on.
- Static: Package
discovery/static. A fixed list of peer addresses. Use when the topology is known upfront. - Kubernetes: Package
discovery/kubernetes. Lists pods through the Kubernetes API. Use inside a Kubernetes cluster. - NATS: Package
discovery/nats. Nodes answer discovery requests over a NATS subject. Use when NATS is already part of the platform. - mDNS: Package
discovery/mdns. Multicast DNS on the local network. Use for development on one LAN. - DNS-SD: Package
discovery/dnssd. Resolves a DNS name to the peers’ addresses. Use when DNS already knows the nodes. - Consul: Package
discovery/consul. Registers the node as a Consul service with a health check. - etcd: Package
discovery/etcd. Registers the node under a leased key. - Self-managed: Package
discovery/selfmanaged. UDP broadcast on the LAN with no backend and no peer list. Use for local clusters and tests.
Static
The static provider returns the configured list as is. Nothing is registered anywhere, so the list must contain every node that may be running, including nodes that are not up yet. Each entry is ahost:port address on the discovery
port.
- Hosts: Required. The peer addresses. Each entry is validated as a TCP address.
Static
Kubernetes
The Kubernetes provider lists pods through the Kubernetes API, matching the configured labels andstatus.phase=Running. Pods do not need to be Ready to be discovered: on a cold start no pod is Ready until its actor
system has joined the cluster, so readiness cannot gate discovery. Terminating pods and pods without an assigned IP are
excluded, and an empty result is treated as a retryable error so that simultaneously starting replicas keep retrying
until they see each other.
The provider uses the in-cluster configuration, so it only runs inside a pod, and the pod’s service account needs
permission to list pods in the namespace:
Role
- Namespace: Required. The namespace to list pods in.
- PodLabels: Required. A pod must carry all of these labels to count as a peer.
- DiscoveryPortName: Required. The name of the container port carrying membership gossip. Peer addresses are built from the pod IP and this port.
- RemotingPortName and PeersPortName: Deprecated and ignored. Peers learn each other’s remoting and peers ports from the node metadata exchanged once they are members. Existing configurations that set them keep working.
Kubernetes
NATS
Each node subscribes to the configured subject and answers discovery requests with its host and discovery port.DiscoverPeers publishes a request on that subject and collects the answers for the configured timeout. On shutdown
the node publishes a departure message. All nodes of one cluster must use the same subject.
- NatsServer: Required. The server address, in the form
nats://host:port. - NatsSubject: Required. The subject the cluster uses for discovery traffic.
- Host and DiscoveryPort: Required. The address this node advertises.
- Timeout: How long
DiscoverPeerswaits for answers. Default 1s. - MaxJoinAttempts: How many times to try connecting to the NATS server before giving up. Default 5.
- ReconnectWait: The backoff between connection attempts. Default 2s.
NewDiscovery also accepts nats.WithLogger to route the provider’s logs to your logger.
NATS
mDNS
Each node advertises a service instance named after the cluster with a unique suffix, and carries the cluster name in its TXT record. Each discovery call browses for five seconds and keeps the instances whose service, domain, port and cluster name match. It returns one IPv4 address per node, plus one IPv6 address whenIPv6 is true. Queries travel
over IPv4 multicast, and IPv6 addresses come from the AAAA records nodes include in their answers, so the network needs
IPv4 multicast even for IPv6 discovery. Multicast must be allowed on the network, which rules out most cloud networks.
- ServiceName: Required. The cluster name. All nodes of one cluster share it, and each node adds its own suffix on the wire.
- Service: Required. The service type, for example
_goakt._tcp. - Domain: Required. The mDNS domain.
localandlocal.are both accepted. - Port: The discovery port this node announces. Peers announcing a different port are ignored.
- IPv6: Optional. When true, IPv6 addresses are returned alongside IPv4 ones.
NewDiscovery also accepts mdns.WithLogger to route the provider’s log output, including the mDNS library’s own
messages, to your logger.
mDNS
DNS-SD
The provider resolves the configured name and returns every address in its A and AAAA records. There is no registration: the DNS records are managed outside GoAkt, for example by a headless Service in Kubernetes, a Docker Compose service name or Consul DNS. The addresses come back without a port, and the membership layer fills in this node’s own discovery port, so every node must listen on the same discovery port.- DomainName: Required. The name to resolve.
- IPv6: Optional. When true, only AAAA records are returned. Otherwise all records are returned.
DNS-SD
Consul
Register creates a Consul service named after the actor system, with the node’s host and discovery port as its
address and the system name as a tag. A TCP health check against that address is attached by default.
DiscoverPeers queries the health endpoint for services with that name and tag, and excludes this node. When a service
has no address of its own, the address of the Consul node hosting it is used.
- Address: Required. The Consul agent, for example
127.0.0.1:8500. - ActorSystemName: Required. Used as the service name and tag, so nodes of different systems do not discover each other.
- Host and DiscoveryPort: Required. The address registered for this node.
- Datacenter: Optional. The datacenter to register in and query. Defaults to the agent’s datacenter.
- Token: Optional. The ACL token for authenticated requests.
- Timeout: The limit for each Consul request. Default 10s.
- Context: Optional. The context Consul requests run under. Defaults to a background context.
- HealthCheck: The TCP check Consul runs against the node.
Intervaldefaults to 10s andTimeoutto 3s. - QueryOptions: How peers are looked up.
OnlyPassingrestricts results to healthy services.AllowStaleaccepts answers from follower agents.WaitTimebounds blocking queries.Nearsorts results by distance to a node.Datacenteroverrides the datacenter for queries. When the field is nil,WaitTimedefaults to 1s and the two flags to false.
Consul
etcd
Register grants a lease of TTL seconds and stores the node’s address under the key <ActorSystemName>/<host:port>
bound to that lease. The lease is kept alive for as long as Context lives, so a node that dies stops renewing and its
entry expires after TTL. DiscoverPeers lists the keys under the system’s prefix and excludes this node.
- Endpoints: Required. The etcd endpoints.
- ActorSystemName: Required. The key prefix, so nodes of different systems do not discover each other.
- Host and DiscoveryPort: Required. The address registered for this node.
- TTL: Required. The lease duration in seconds. It bounds how long a dead node stays visible.
- DialTimeout: Required. The limit for connecting to etcd.
- Timeout: Required. The limit for each etcd request.
- Context: Optional. Must outlive the provider: cancelling it stops the keep-alive and removes the node from etcd. Defaults to a background context.
- TLS, Username, Password: Optional. Client TLS and authentication.
etcd
Self-managed (zero config)
The self-managed provider uses UDP broadcast for peer discovery. No third-party services or peer IPs are required; nodes on the same LAN discover each other automatically. Each node broadcasts its address at every interval and keeps a cache of the peers it has heard from. A peer silent for three intervals is dropped. Broadcast does not cross subnets, so this suits local clusters and tests rather than cloud deployments.- ClusterName: Required. Only nodes with the same name discover each other.
- SelfAddress: Required. The
host:portthis node advertises, on the discovery port. - BroadcastPort: The UDP port for announcements. Default 7947.
- BroadcastInterval: How often this node announces itself. Default 5s.
- BroadcastAddress: The target for announcements. Default
255.255.255.255. Set it to127.0.0.1to keep discovery on the loopback interface, for example in tests.
Self-managed (zero config)
Configuration
Configuration
WithRemote) for cross-node communication.
Discovery happens at boot
A node uses its provider when it boots, in this order: it starts listening for membership gossip, callsRegister,
then calls DiscoverPeers and joins the nodes it returns. From then on membership spreads through gossip between the
members, not through the provider.
What DiscoverPeers returns decides how the boot continues:
What happens after boot depends on
WithMinimumPeersQuorum:
- Quorum of 1 (the default): the node serves as soon as it has booted and does not query the provider again. A node that booted alone stays alone until another node joins it.
- Quorum of 2 or more: the node serves nothing until the cluster has that many members, and while it is below the quorum it queries the provider again every five seconds. A node that found nobody at boot keeps looking.
The two rules a provider must follow
Nodes that boot at the same moment end up in one cluster when the provider guarantees both of these:- A node is visible before it reads. Once
Registerhas returned on a node,DiscoverPeerson the other nodes lists it. - Reads are consistent.
DiscoverPeersreturns every node whoseRegisterreturned before the call.
AllowStale or OnlyPassing depend on how fast their backend propagates a
new node. Use a quorum of 2 or more with those when nodes can boot at the same moment.
When the view may be incomplete, return an error
A provider that cannot guarantee both rules must not answer with a list that holds no other node while its view may still be catching up. It returns an error instead, which makes the node retry for up to ten seconds. A node that is really alone, such as the first node of a new cluster or a single-node deployment, then boots after those ten seconds. The wrapper below adds that behavior to any provider:requirePeers
Wiring
The wrapper covers a view that catches up within ten seconds. It does not help a provider that lists a node only
once it is ready: no node becomes ready while it is still retrying, so each one waits ten seconds and then boots
alone. Such a provider has to list nodes that are still booting.
playground/issue-1408 runs these cases with three nodes that boot at the same moment.
Custom provider
Implementdiscovery.Provider and pass it to WithDiscovery. See
Extending GoAkt for the interface and wiring. A custom provider must
follow the two rules, or return an error while its view may be incomplete.