What's Actually Happening
Consul service registration fails when the Consul agent cannot accept registration requests or the service definition is invalid. Services don't appear in Consul catalog, preventing service discovery.
The Error You'll See
Registration error:
```bash $ curl -X PUT http://localhost:8500/v1/agent/service/register -d @service.json
rpc error making call: RPC error making call: The requested service "my-service" is not registered ```
Service not listed:
```bash $ curl http://localhost:8500/v1/agent/services
{} # Empty, service not registered ```
Consul logs:
```bash $ docker logs consul
2026-04-16T00:39:00Z [ERROR] agent: Service registration failed: service=my-service error="Check 'service:my-service' has invalid TTL" ```
Why This Happens
- 1.Invalid service definition - Malformed JSON or missing required fields
- 2.Agent not running - Consul agent stopped or crashed
- 3.Network connectivity - Cannot reach Consul agent
- 4.ACL permissions - Token lacks registration permission
- 5.Service ID conflict - Service ID already registered
- 6.Invalid check definition - Health check configuration error
Step 1: Check Consul Agent Status
```bash # Check Consul agent is running consul members
# Check agent status consul info
# Check if agent is leader consul operator raft list-peers
# Check agent health curl http://localhost:8500/v1/agent/self
# Check if agent is in server or client mode consul info | grep server
# Check datacenter consul info | grep datacenter
# Check if agent is alive ps aux | grep consul docker ps | grep consul
# View agent logs consul monitor -log-level=debug ```
Step 2: Test Connectivity
```bash # Test HTTP API curl http://localhost:8500/v1/status/leader
# Test from application server curl http://consul-server:8500/v1/agent/self
# Check DNS dig @localhost -p 8600 consul.service.consul
# Test TCP connectivity nc -zv localhost 8500
# Check firewall iptables -L -n -v | grep 8500
# Allow Consul ports iptables -I INPUT -p tcp --dport 8500 -j ACCEPT # HTTP API iptables -I INPUT -p tcp --dport 8300 -j ACCEPT # Server RPC iptables -I INPUT -p tcp --dport 8301 -j ACCEPT # LAN Serf iptables -I INPUT -p tcp --dport 8302 -j ACCEPT # WAN Serf iptables -I INPUT -p udp --dport 8301 -j ACCEPT # LAN Serf UDP iptables -I INPUT -p udp --dport 8600 -j ACCEPT # DNS ```
Step 3: Validate Service Definition
```json // Check service.json is valid JSON { "ID": "my-service-1", "Name": "my-service", "Tags": ["v1", "production"], "Address": "10.0.0.1", "Port": 8080, "Meta": { "version": "1.0.0" }, "EnableTagOverride": false, "Check": { "HTTP": "http://10.0.0.1:8080/health", "Interval": "10s", "Timeout": "1s" }, "Weights": { "Passing": 10, "Warning": 1 } }
// Validate JSON cat service.json | jq .
// Common issues: // 1. Missing "Name" field (required) // 2. Invalid JSON syntax // 3. "Port" must be integer, not string // 4. Check "Interval" must be duration string ```
Step 4: Register Service Correctly
```bash # Register via HTTP API curl -X PUT http://localhost:8500/v1/agent/service/register -d @service.json
# Register via CLI consul services register service.json
# Verify registration curl http://localhost:8500/v1/agent/services | jq .
# Check specific service curl http://localhost:8500/v1/agent/service/my-service-1 | jq .
# List all services in catalog curl http://localhost:8500/v1/catalog/services | jq .
# Get service instances curl http://localhost:8500/v1/catalog/service/my-service | jq .
# Deregister service curl -X PUT http://localhost:8500/v1/agent/service/deregister/my-service-1 ```
Step 5: Fix Health Check Issues
```json // Invalid check configuration { "Name": "my-service", "Check": { "TTL": "30s" // TTL check needs manual updates } }
// TTL checks require periodic updates // curl -X PUT http://localhost:8500/v1/agent/check/pass/service:my-service-1
// Better: Use HTTP check { "Name": "my-service", "Port": 8080, "Check": { "HTTP": "http://localhost:8080/health", "Interval": "10s", "Timeout": "5s", "DeregisterCriticalServiceAfter": "1m" // Auto-deregister if critical } }
// TCP check { "Name": "my-service", "Port": 8080, "Check": { "TCP": "localhost:8080", "Interval": "10s", "Timeout": "5s" } }
// gRPC check { "Name": "my-service", "Port": 9090, "Check": { "GRPC": "localhost:9090", "Interval": "10s" } } ```
Step 6: Check ACL Permissions
```bash # If ACLs enabled, check token has permission
# Get token consul acl token list
# Check token permissions consul acl token read -id <token-id>
# Create token with service registration permission consul acl policy create -name "service-register" -rules @policy.hcl
# policy.hcl agent_prefix "" { policy = "write" } service_prefix "" { policy = "write" }
# Create token with policy consul acl token create -description "Service registration token" -policy-name service-register
# Use token in requests curl -X PUT http://localhost:8500/v1/agent/service/register?token=<token> -d @service.json
# Or set CONSUL_HTTP_TOKEN export CONSUL_HTTP_TOKEN=<token> consul services register service.json ```
Step 7: Fix Service ID Conflicts
```bash # Check for duplicate service IDs curl http://localhost:8500/v1/agent/services | jq 'keys'
# Deregister conflicting service curl -X PUT http://localhost:8500/v1/agent/service/deregister/my-service-1
# Use unique IDs per instance { "ID": "my-service-10.0.0.1", // Include IP or unique identifier "Name": "my-service", // Same name for service discovery "Address": "10.0.0.1", "Port": 8080 }
# Multiple instances { "ID": "my-service-1", "Name": "my-service", "Address": "10.0.0.1", "Port": 8080 } --- { "ID": "my-service-2", "Name": "my-service", "Address": "10.0.0.2", "Port": 8080 } ```
Step 8: Use Service Definitions Directory
```bash # Place service definitions in Consul config directory # /etc/consul.d/services/
# /etc/consul.d/services/web.json { "service": { "name": "web", "port": 80, "check": { "http": "http://localhost/health", "interval": "10s" } } }
# Start Consul with config dir consul agent -config-dir=/etc/consul.d
# Services are automatically registered on startup
# Reload configuration consul reload
# Check loaded services consul config list services ```
Step 9: Use Consul Connect for Service Mesh
```json // Register with Connect (service mesh) { "Name": "my-service", "Port": 8080, "Connect": { "SidecarService": { "Port": 20000, "Check": { "TCP": "127.0.0.1:20000", "Interval": "10s" } } }, "Check": { "HTTP": "http://localhost:8080/health", "Interval": "10s" } }
// Start Envoy sidecar consul connect proxy -sidecar-for my-service-1
// Register upstream dependency { "Name": "web", "Port": 8080, "Connect": { "SidecarService": { "Proxy": { "Upstreams": [ { "DestinationName": "api", "LocalBindPort": 9090 } ] } } } } ```
Step 10: Monitor Service Registration
```bash # Watch for service changes consul watch -type=services -handler="echo 'Services changed'"
# Check service health curl http://localhost:8500/v1/health/service/my-service | jq '.[].Checks'
# Create monitoring script cat << 'EOF' > /usr/local/bin/check_consul_services.sh #!/bin/bash SERVICES=$(curl -s http://localhost:8500/v1/agent/services) COUNT=$(echo $SERVICES | jq 'length')
if [ "$COUNT" -eq 0 ]; then echo "WARNING: No services registered" fi
echo "Registered services: $COUNT" echo $SERVICES | jq -r 'keys[]' EOF
chmod +x /usr/local/bin/check_consul_services.sh
# Prometheus metrics # Enable Prometheus metrics endpoint # /etc/consul.d/config.json { "telemetry": { "prometheus_retention_time": "24h", "disable_hostname": true } }
# Access metrics curl http://localhost:8500/v1/agent/metrics?format=prometheus ```
Consul Service Registration Checklist
| Check | Command | Expected |
|---|---|---|
| Agent running | consul members | Node listed |
| API accessible | curl /v1/agent/self | JSON response |
| Valid JSON | jq . service.json | No errors |
| Service listed | /v1/agent/services | Service visible |
| Health check | /v1/health/service | Status: passing |
Verify the Fix
```bash # After fixing registration issues
# 1. Check service registered curl http://localhost:8500/v1/agent/services | jq . # Should show service details
# 2. Check service in catalog curl http://localhost:8500/v1/catalog/service/my-service | jq . # Should show service instance
# 3. Verify health check curl http://localhost:8500/v1/health/service/my-service | jq '.[].Checks' # Should show Status: "passing"
# 4. Test DNS resolution dig @localhost -p 8600 my-service.service.consul # Should return service IP
# 5. Test service discovery curl http://localhost:8500/v1/catalog/service/my-service # Should return service address and port
# 6. Check logs for errors consul monitor | grep -i error # No registration errors ```
Related Issues
- [Fix Consul Agent Not Starting](/articles/fix-consul-agent-not-starting)
- [Fix Consul DNS Resolution Failed](/articles/fix-consul-dns-resolution-failed)
- [Fix Consul Health Check Failing](/articles/fix-consul-health-check-failing)