executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
consulHashiCorp Consul for service discovery, health checking, and service mesh. Use when the user needs to register services, perform health checks, store configuration in the KV store, or set up Connect for secure service-to-service communication.
Independent GitHub snapshot · Automated classification · Original documentation governs
Consul provides service discovery, health checking, KV storage, and service mesh capabilities. The current release line is 2.x (2.0.4 as of September 2026); the commands below work the same on 1.x. Since 2023 Consul is under the Business Source License, not an open-source one: check the terms before offering it as a hosted product. Never run a production agent with -dev, which keeps everything in memory with no ACLs or TLS.
# Install Consul
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install consul
# macOS alternative: brew tap hashicorp/tap && brew install hashicorp/tap/consul
# Start dev agent
consul agent -dev
# Check cluster members
consul members
# consul-server.hcl — Production Consul server config
datacenter = "dc1"
data_dir = "/opt/consul/data"
log_level = "INFO"
server = true
bootstrap_expect = 3
bind_addr = "{{ GetPrivateInterfaces | include \"network\" \"10.0.0.0/8\" | attr \"address\" }}"
client_addr = "0.0.0.0"
ui_config { enabled = true }
connect { enabled = true }
ports {
grpc = 8502 # plaintext xDS for Envoy on the same host; disabled (-1) by default
grpc_tls = 8503 # TLS xDS for proxies on other hosts
}
# generate with: consul keygen
encrypt = "pUqJrVyVRj5jsiYEkM/tFQYfWyJIv4s3XkvDwy7Cu5s="
acl {
enabled = true
default_policy = "deny"
enable_token_persistence = true
tokens {
# agent token: needs node "<name>" write + service_prefix "" read
agent = "0b4f3c1e-7a52-4d0e-9c1b-5d2f8e6a1b34"
# default token for DNS and unauthenticated requests: read-only
default = "6e1d9a20-3c4b-4f77-8a0e-2b9c7d51f0aa"
}
}
tls {
defaults {
ca_file = "/opt/consul/tls/ca.pem"
cert_file = "/opt/consul/tls/server.pem"
key_file = "/opt/consul/tls/server-key.pem"
verify_incoming = true
verify_outgoing = true
}
internal_rpc {
verify_server_hostname = true
}
}
autopilot {
cleanup_dead_servers = true
last_contact_threshold = "200ms"
server_stabilization_time = "10s"
}
// services/web.json — Register web service with health check
{
"service": {
"name": "web",
"port": 8080,
"tags": ["production", "v2"],
"meta": {
"version": "2.0.0"
},
"check": {
"http": "http://localhost:8080/health",
"interval": "10s",
"timeout": "5s",
"deregister_critical_service_after": "30m"
}
}
}
consul services register services/web.json
consul services deregister -id=web
dig @127.0.0.1 -p 8600 web.service.consul SRV
curl http://localhost:8500/v1/health/service/web?passing=true
consul catalog services
consul catalog nodes
# Key-value operations
consul kv put config/app/db_host "orders-db.internal.shopfront.io"
consul kv put config/app/db_port "5432"
consul kv get config/app/db_host
consul kv get -recurse config/app/
consul kv export config/ > backup.json
consul kv import @backup.json
consul kv delete config/app/db_host
consul kv delete -recurse config/app/
consul watch -type=key -key=config/app/db_host /scripts/reload.sh
# connect-proxy.hcl — Service with sidecar proxy
service {
name = "web"
port = 8080
connect {
sidecar_service {
proxy {
upstreams {
destination_name = "api"
local_bind_port = 9091
}
upstreams {
destination_name = "database"
local_bind_port = 9092
}
}
}
}
}
Register the sidecar, then start Envoy next to the service (Envoy must be installed; the agent needs the gRPC port from the config above):
consul services register connect-proxy.hcl
consul connect envoy -sidecar-for web -token-file=/etc/consul.d/web.token
# intentions.hcl — Service intentions for access control
Kind = "service-intentions"
Name = "api"
Sources = [
{
Name = "web"
Action = "allow"
},
{
Name = "*"
Action = "deny"
}
]
# Manage intentions
consul config write intentions.hcl
consul config read -kind service-intentions -name api
consul intention check web api
The consul intention subcommands (create, list, ...) are deprecated since 1.9 in favour of the service-intentions config entry shown above; intention check is still the quick way to test a pair.
# Create prepared query for failover across datacenters
curl -X POST http://localhost:8500/v1/query \
-H "X-Consul-Token: $CONSUL_HTTP_TOKEN" -d '{
"Name": "web-query",
"Service": {
"Service": "web",
"Tags": ["production"],
"Failover": {
"Datacenters": ["dc2", "dc3"]
},
"OnlyPassing": true
}
}'
# Bootstrap ACL system (once; save the SecretID, e.g. export CONSUL_HTTP_TOKEN)
consul acl bootstrap
# Create policy
consul acl policy create -name "app-read" \
-rules='service_prefix "web" { policy = "read" } key_prefix "config/app" { policy = "read" }'
# Create token with policy
consul acl token create -description "App token" -policy-name "app-read"
# Cluster management
consul join 10.0.1.10
consul leave
consul operator raft list-peers
consul operator autopilot state
# Snapshots for backup
consul snapshot save backup.snap
consul snapshot restore backup.snap
consul monitor -log-level=debug
User request: "Register my storefront API on port 8080 with a health check and look it up through DNS."
consul services register services/web.json
dig @127.0.0.1 -p 8600 web.service.consul SRV
curl -s "http://localhost:8500/v1/health/service/web?passing=true"
The SRV answer lists the node and port 8080 only while the /health check passes; a failing check removes the instance from the answer.
User request: "Only the web service may call api in our mesh."
consul config write intentions.hcl
consul intention check web api # Allowed
consul intention check cart api # Denied
acl.default_policy = "deny", set acl.tokens.agent and acl.tokens.default; otherwise registration, anti-entropy and DNS fail with permission errors. Pass tokens through CONSUL_HTTP_TOKEN or -token-file, never inline in shell history.client_addr to 0.0.0.0 without ACLs and TLS: the HTTP API then controls the cluster.bootstrap_expect), never an even count. Take consul snapshot save backups on a schedule and test restores.consul keyring.deregister_critical_service_after so dead instances do not linger.