ops.json in each
service that should be part of the cluster.
This page assumes your service is already deploying on LocalOps. If you’re still packaging your app, start with the
Elixir/Phoenix guide.
Enable it
ops.json
What LocalOps injects
Two environment variables are injected into your pods:app-services is the Kubernetes namespace all your services run in within the environment.
Wire up libcluster
Read the DNS name from the environment — don’t hardcode it.config/runtime.exs
Your release config stays yours
LocalOps does not setRELEASE_DISTRIBUTION, RELEASE_NODE, or RELEASE_COOKIE. Set these yourself — typically
RELEASE_DISTRIBUTION=name with RELEASE_NODE=myapp@${POD_IP}. POD_IP is already injected into every service, so you
can reference it directly.
Cluster across regions
The headless service above only resolves inside one environment’s Kubernetes cluster, so nodes in your Mumbai environment can’t see nodes in Ireland. To form a single mesh across regional environments, give each environment a DNS name in a private zone that every regional VPC can resolve, and let LocalOps keep that name pointed at the environment’s pods.Create one private zone shared by every regional VPC
On AWS, create a Route 53 private hosted zone — say
mesh.acmecorp.internal — and associate it with the VPC of
every regional environment you spin up. One zone, many VPC associations. If your environments live in different AWS
accounts, use cross-account zone association. On GCP, the equivalent is a Cloud DNS private zone with each regional
VPC added to its visibility list.The zone has to be associated with a region’s VPC before nodes there can resolve peers.Give each environment a name in ops.json
Alongside Keep
beam_cluster_id, set beam_dns_name to the record this environment should own inside that zone:ops.json
beam_cluster_id identical everywhere — it’s still what makes these nodes one cluster. beam_dns_name is
per environment: mumbai.mesh.acmecorp.internal in Mumbai, ireland.mesh.acmecorp.internal in Ireland, and so on.LocalOps creates that record and keeps it in sync — it publishes the IPs of every pod in the environment carrying
that beam_cluster_id, and updates the record as pods scale, restart, or get rescheduled. You don’t manage the
record set yourself.List every region in your topologies
Add one topology per region so libcluster polls all of them. The same config ships to every region — each node finds its local peers through
LOPS_BEAM_CLUSTER_DNS still points at the local
headless service, so keep using it for this environment’s own nodes and name the peer regions by their
beam_dns_name:config/runtime.exs
LOPS_BEAM_CLUSTER_DNS and every
remote region through that region’s record. Nodes connect from both ends, and the mesh forms.Preview environments ignore
beam_dns_name. They keep forming their own single-region cluster, for the same reason
they stay out of your production cluster.Things to know
It takes effect on your next build
LocalOps snapshotsops.json when it builds your service, so redeploying your current image won’t pick up a newly added
beam_cluster_id. Push the change and let it build.
Preview environments get their own cluster
A preview environment forms its own separate cluster and never joins your production cluster. This is deliberate — otherwise a preview would share your cookie and be able to call into live nodes. See ephemeral databases and services for pull request previews.Job and cron services join too
Job and cron services join the cluster as well, so scheduled and one-off tasks can call into the running cluster. If you’d rather they stayed out, just leavebeam_cluster_id out of those services’ ops.json.
Processes with their own image stay out
Aprocesses entry that specifies its own image stays out of
the cluster, since it isn’t running your app.
Verify it worked
Get a shell on a running pod (see Shell into a pod), open a remote console, and run:LOPS_BEAM_CLUSTER_NAME.
To watch inter-node distribution traffic over time, see the
Erlang & Elixir instrumentation guide — the Erlang-Distribution Grafana
dashboard it links is built for exactly this.