RabbitMQ Replica Node Stays Offline After Auto‑Scaling in us-east-1
Problem
In a multi‑region deployment that uses RabbitMQ federation and mirrored queues, the auto‑scaler (AWS Auto Scaling Group) successfully launches new EC2 instances in us-east-1. The instances boot, the RabbitMQ service starts, but the node never joins the existing cluster. The node remains in offline state in the management UI and rabbitmqctl cluster_status shows it as a disc node that is not part of the cluster.
Typical symptoms:
- Auto‑scaling event logs show instance
i-0abcd1234efgh5678launched. - Systemd logs:
systemd[1]: Started RabbitMQ broker. - RabbitMQ logs contain:
2024-10-07 12:03:45.123 [error] <0.1234.0> failed to join cluster: {cannot_connect, {node_not_found, 'rabbit@ip-10-0-0-45.us-east-1.compute.internal'}}
rabbitmqctl statuson the new node reports{running_applications,[]}and{cluster_status,[]}.- Federation links continue to work for existing nodes, but the new replica never receives mirrored queue data.
Root Cause
The failure originates from a mismatch between the cluster name and the node name resolution performed during the join process. The auto‑scaled instances are launched with the default hostname generated by the EC2 DNS resolver (ip-10-0-0-45). RabbitMQ, when started with the default RABBITMQ_NODENAME=rabbit@$(hostname -s), registers itself as rabbit@ip-10-0-0-45. However, the existing cluster nodes were started with a fully qualified domain name (FQDN) derived from the aws_route53_record that resolves to the private IP, e.g. rabbit@rabbit-us-east-1.internal.
Because the new node advertises a different name, the existing nodes cannot resolve it, and the Erlang distribution layer rejects the join request with node_not_found. The problem is amplified by the fact that the cluster_formation.classic_config section in rabbitmq.conf lists static node names (FQDNs) and does not include the auto‑generated hostname.
Additionally, the security groups allow traffic only on the private subnet CIDR. When the new node attempts to resolve the FQDN of the existing nodes, the DNS query falls back to the public resolver, returning a public IP that is blocked by the security group, causing a second failure mode: connection refused.
Investigation and Debugging
Follow these steps to reproduce the failure and collect evidence.
- Confirm node name mismatch:
# On the new node hostname -s # Expected: rabbit-us-east-1-01 # Actual: ip-10-0-0-45 # Show RabbitMQ node name sudo rabbitmqctl status | grep -i nodename # Output: {nodename,<<"rabbit@ip-10-0-0-45">>} - Check DNS resolution from the new node:
# Resolve an existing cluster node nslookup rabbit-us-east-1-01.internal # Expected private IP (10.0.x.x) # Actual: NXDOMAIN or public IP - Inspect cluster formation config:
# /etc/rabbitmq/rabbitmq.conf cluster_formation.classic_config.nodes.1 = rabbit@rabbit-us-east-1-01.internal cluster_formation.classic_config.nodes.2 = rabbit@rabbit-us-east-1-02.internal - Review security group rules:
# Verify inbound Erlang port (25672) allows traffic from the new node's private IP aws ec2 describe-security-groups --group-ids sg-0abc1234def56789 - Collect Erlang distribution logs:
journalctl -u rabbitmq-server -n 50 | grep -i distribution 2024-10-07 12:03:45.123 rabbit@ip-10-0-0-45 ... failed to join cluster: {cannot_connect, {node_not_found, 'rabbit@rabbit-us-east-1-01.internal'}}
Solution
Standardize node naming and ensure DNS resolution to private IPs before the RabbitMQ service starts. The fix consists of three parts:
1. Set a deterministic node name using the instance tag
Update the launch template/user data to export RABBITMQ_NODENAME based on the Name tag that follows the rabbit- pattern.
# User data (cloud‑init)
#!/bin/bash
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
NAME=$(aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" "Name=key,Values=Name" --region us-east-1 --output text | cut -f5)
export RABBITMQ_NODENAME="rabbit@$NAME.internal"
echo "RABBITMQ_NODENAME=$RABBITMQ_NODENAME" >> /etc/rabbitmq/rabbitmq-env.conf
2. Ensure private DNS resolution
Enable enableDnsHostnames and enableDnsSupport on the VPC, and create a private Route 53 hosted zone that maps *.internal to the private IPs of each node. Add an A record for each node using the same Name tag.
3. Adjust rabbitmq.conf to use a dynamic node list
Replace the static classic_config.nodes list with the cluster_formation.peer_discovery_backend = rabbit_peer_discovery_aws plugin, which discovers peers via AWS tags.
# /etc/rabbitmq/rabbitmq.conf (before)
cluster_formation.classic_config.nodes.1 = rabbit@rabbit-us-east-1-01.internal
cluster_formation.classic_config.nodes.2 = rabbit@rabbit-us-east-1-02.internal
# /etc/rabbitmq/rabbitmq.conf (after)
cluster_formation.peer_discovery_backend = rabbit_peer_discovery_aws
cluster_formation.aws.region = us-east-1
cluster_formation.aws.tag_key = Name
cluster_formation.aws.tag_value = rabbit-us-east-1
cluster_formation.aws.use_private_ip = true
After updating the launch template, perform a rolling replacement of the Auto Scaling Group so that new instances start with the corrected environment variables and configuration.
Verification
Validate that newly provisioned nodes join the cluster automatically.
- Trigger a scale‑out event (e.g., increase desired capacity by 1).
- On the new instance, confirm the node name:
sudo rabbitmqctl status | grep -i nodename # Expected: {nodename,<<"rabbit@rabbit-us-east-1-03.internal">>} - Check cluster status from any existing node:
sudo rabbitmqctl cluster_status # Output should list the new node under {running_nodes, [...]} - Verify federation links are healthy:
curl -s http://localhost:15672/api/federation-links -u guest:guest | jq '.[] | select(.status=="running")' - Inspect RabbitMQ logs for successful join messages:
grep -i "joined cluster" /var/log/rabbitmq/rabbit@*.log 2024-10-07 12:15:02.789 [info] <0.5678.0> node 'rabbit@rabbit-us-east-1-03.internal' joined the cluster
Prevention and Best Practices
- Use peer discovery plugins (AWS, Kubernetes, Consul) instead of hard‑coded node lists for dynamic environments.
- Enforce a naming convention via EC2 tags and propagate it to
RABBITMQ_NODENAMEat boot time. - Validate DNS resolution in a pre‑start health check script; abort startup if the node cannot resolve any peer.
- Monitor Erlang distribution metrics (
rabbitmq_erlang_dist_node_up,rabbitmq_erlang_dist_node_down) and set alerts for sudden spikes. - Restrict security groups to allow Erlang port (25672) and inter‑region federation ports (5671/5672) only from the VPC CIDR.
- Automated smoke test in the ASG lifecycle hook: after instance becomes
InService, runrabbitmqctl cluster_statusand report success/failure to CloudWatch.
FAQ
- Why does the node appear “offline” even though the service is running?
Because RabbitMQ registers itself with a hostname that existing cluster members cannot resolve, the Erlang distribution layer rejects the join request, leaving the node in an isolated state. - Can I keep the static
classic_config.nodeslist and still auto‑scale?
It is possible by adding a script that updates the list on every scale‑out, but using a peer‑discovery plugin eliminates manual list maintenance and reduces configuration drift. - What ports must be opened between regions for federation to work?
Erlang distribution (TCP 25672) for cluster formation, and the AMQP ports used by federation (5671 for TLS, 5672 for plain) must be reachable over the private VPC peering or Transit Gateway. - How do I troubleshoot a “node_not_found” error after fixing DNS?
Check that theRABBITMQ_NODENAMEenvironment variable matches the DNS record, and verify that the node can resolve its own FQDN (runhostname -f). Also confirm that the security group permits inbound traffic from the peer’s private IP on port 25672. - Is there a way to see which cipher suite is used for federation TLS connections?
Enablessl_options` in the federation upstream definition and inspect the connection logs:2024-10-07 12:20:11.345 [info] <0.1234.0> TLS handshake succeeded, cipher: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Related Topic Hub: Data Infrastructure Troubleshooting Hub