libcluster strategy that discovers peers by polling the local Tailscale
daemon via tailscale status --json.
Unlike strategies built on the Tailscale cloud API, this works with any control server speaking the Tailscale protocol — including self-hosted Headscale and Headscale-based products — because peer information comes from the node's own daemon, and requires no API key.
The name follows tailscaled's own "LocalAPI" and deliberately avoids
clashing with Cluster.Strategy.Tailscale from
libcluster_tailscale,
which discovers peers through the Tailscale cloud API instead; the two can
coexist in one project. This strategy requires tailscaled to be running
on the node itself — for nodes that reach the tailnet only indirectly
(e.g. via a subnet router), use the cloud API strategy. See the README for
a full comparison.
Usage
Start every node with a longname built from its mesh IPv4 and a shared
basename (e.g. myapp@100.64.0.7), then configure:
config :libcluster,
topologies: [
tailnet: [
strategy: Cluster.Strategy.TailscaleLocal,
config: [
node_basename: "myapp",
hostnames: ["myapp-"],
# cli: "/Applications/Tailscale.app/Contents/MacOS/Tailscale",
# polling_interval: 5_000
]
]
]Options
node_basename(required) — node name part before@, shared by all nodeshostnames(required) — list of mesh hostname prefixes that identify cluster members; peers not matching any prefix are ignoredcli— path to thetailscalebinary (default"tailscale")polling_interval— poll interval in milliseconds (default5_000)cmd_timeout— CLI timeout in milliseconds (default10_000; polls never overlap regardless, as the next poll is scheduled after the current one)runner—LibclusterMesh.Runnerimplementation (default runs the CLI)