Register a Consul Service with a TTL Health Check and Verify DNS Visibility
Use the Consul agent HTTP API to register a service with a TTL health check, verify catalog and DNS visibility, and recover from critical checks or deregistration.
06 Aug 2026, 07:04 UTC

Why stale instances are the real problem
A Consul catalog can keep advertising a service instance long after the process behind it has exited. The entry remains in DNS responses and in any downstream discovery or load-balancing integration until an operator removes it. The useful takeaway is that a TTL health check makes visibility conditional: the instance is discoverable only while a heartbeat is current, and a deregistration grace period removes it automatically when the heartbeat stops.
This guide walks through registering a single service instance with a TTL check via the local agent HTTP API, verifying catalog and DNS visibility, and recovering from critical checks or deregistration. The examples assume a Consul 1.x agent. TTL field names and defaults are version-sensitive, so confirm the exact schema for your release before relying on specific values.
Prerequisites and permissions
- A running Consul agent reachable on its HTTP port, commonly
8500. - A stable service name and a unique service ID. The ID is what the agent uses to track the local registration.
- If ACLs are enabled, a token with
service:writefor registration and deregistration, plusservice:readandnode:readfor verification queries. - A heartbeat mechanism—a process supervisor, cron job, or sidecar—that can issue an HTTP call at an interval shorter than the TTL.
Run all commands from a host that can reach the agent HTTP port. Replace AGENT_HOST with the agent address and TOKEN with your ACL token. If ACLs default to deny, a missing service:write permission causes registration to fail; check the HTTP status code rather than assuming success.
Register the service with a TTL check
Create a JSON payload that describes the instance and its check. The Check object uses a TTL field instead of an HTTP or script probe. The example below uses a 30-second TTL and a one-hour deregistration grace period; treat both as placeholders and verify the supported field names for your version.
cat > /tmp/web-service.json <<'EOF'
{
"Name": "web",
"ID": "web-1",
"Address": "10.0.0.5",
"Port": 8080,
"Check": {
"TTL": "30s",
"DeregisterCriticalServiceAfter": "1h"
}
}
EOF
Submit it to the agent registration endpoint:
curl -s -X PUT http://AGENT_HOST:8500/v1/agent/service/register -H 'X-Consul-Token: TOKEN' -d @/tmp/web-service.json
A successful call returns HTTP 200. If the agent rejects the payload, inspect the status code and response body before retrying. Common causes are insufficient ACL permissions, a duplicate service ID, or a field name that does not exist in your Consul version.
Keep the check passing with heartbeats
Registration alone does not make the check pass. The agent expects a heartbeat on the check-pass endpoint. The check ID for a service-level TTL check is service:<service-id>. Because the colon is a reserved path character, URL-encode it as %3A to avoid request failures behind strict proxies or clients.
curl -s -X PUT http://AGENT_HOST:8500/v1/agent/check/pass/service%3Aweb-1 -H 'X-Consul-Token: TOKEN'
Schedule this call at an interval comfortably below the TTL—for example every 10 seconds for a 30-second TTL. If the heartbeat is late, the check transitions to critical and the instance stops being returned by passing-only queries.
Verify catalog and DNS visibility
First confirm the catalog view. The health endpoint filtered to passing=true should list the instance only while the TTL check is passing:
curl -s http://AGENT_HOST:8500/v1/health/service/web?passing=true -H 'X-Consul-Token: TOKEN'
Next confirm DNS. Consul listens for DNS on port 8600 by default. An A record returns the service address, and an SRV record returns the port:
dig A web.service.consul @AGENT_HOST -p 8600
dig SRV web.service.consul @AGENT_HOST -p 8600
The A response should contain the address from registration, and the SRV response should contain the port. If the catalog shows the instance but DNS does not, verify that you are querying the agent DNS port and that the service name is fully qualified with the .service.consul suffix.
Recover from critical checks and deregistration
If the heartbeat stops past the TTL, the check becomes critical. When DeregisterCriticalServiceAfter is set, the agent removes the instance from the catalog after that grace period. The agent does not re-add a deregistered service automatically; once the underlying process is healthy again, re-run the registration call to restore visibility.
To remove the instance manually, call the deregister endpoint:
curl -s -X PUT http://AGENT_HOST:8500/v1/agent/service/deregister/web-1 -H 'X-Consul-Token: TOKEN'
Be aware of anti-entropy: the agent periodically re-syncs its local registrations into the catalog. If you deregister the catalog entry directly while the local agent registration still exists, anti-entropy can re-create it. Removing the agent-managed service with the endpoint above clears the local state so the removal sticks.
Persistence after agent restart
Registrations made through the HTTP API are ephemeral. They disappear when the agent restarts. For durable services, define the service and check in an agent configuration file and reload the agent instead. After changing the registration method, restart the agent and re-check the catalog to confirm whether the instance persisted.
Limitations and practical checks
- Version sensitivity: TTL semantics and the
DeregisterCriticalServiceAfterfield are version-sensitive. Confirm exact field names, units, and defaults against your Consul release. - Not a singleton guard: Do not use a TTL check as the only signal for a singleton or stateful workload. Pair it with a process supervisor or readiness probe so the process is actually restarted or gated.
- ACL default deny: If ACLs default to deny, a token without
service:writefails registration silently to the caller unless the status code is checked. - URL encoding: Check IDs containing colons must be URL-encoded in the pass, warn, and fail paths or the heartbeat request fails.
A practical verification sequence is: register and confirm HTTP 200; poll /v1/health/service/<name>?passing=true until the check reports passing; query A and SRV records on port 8600; stop the heartbeat past the TTL and confirm the check turns critical and the instance disappears after the grace period; finally, restart the agent to confirm whether the chosen registration method persisted.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.