Command Palette

Search for a command to run...

GitHubGitHub

HashiCorp Consul Installation Guide

Install Consul 2.0, run a client-server cluster with ACLs and TLS, register services with health checks, and set up service discovery and the service mesh.

Consul is a service networking platform. It provides service discovery, distributed configuration, health checking and — with Connect — mutual TLS between every service in your mesh.

System Requirements

  • A 64-bit operating system (Linux, macOS, Windows, FreeBSD)
  • 2 GB RAM for a small cluster, 4 GB or more per server in production
  • Ports 8300 (server RPC), 8301 (serf LAN/WAN), 8302 (Serf), 8500 (HTTP API and UI), 8600 (DNS) and 443/8200 (Consul Connect)
  • A writable data directory for each agent

Installation Instructions

Ubuntu / Debian (apt)

# Add the HashiCorp signing key
wget -O - https://apt.releases.hashicorp.com/gpg \
  | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
 
# Add the repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(grep -oP '(?<=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main" \
  | sudo tee /etc/apt/sources.list.d/hashicorp.list
 
# Install
sudo apt update
sudo apt install -y consul
 
consul version

Configuration

Server

Create /etc/consul.d/server.hcl:

datacenter = "dc1"
data_dir   = "/opt/consul"
node_name  = "consul-server-1"
 
server = true
bootstrap_expect = 3
 
bind_addr        = "{{GetPrivateIP}}"
client_addr      = "0.0.0.0"
advertise_addr   = "{{GetInterfaceIP \"ens5\"}}"
 
retry_join = [
  "10.0.0.11",
  "10.0.0.12",
  "10.0.0.13",
]
 
ui_config {
  enabled = true
}
 
# Deny by default — every service needs an explicit intention
acl {
  enabled                  = true
  default_policy           = "deny"
  enable_token_persistence = true
}

Client

Create /etc/consul.d/client.hcl:

datacenter = "dc1"
data_dir   = "/opt/consul"
node_name  = "app-server-1"
 
server = false
 
bind_addr = "{{GetPrivateIP}}"
 
retry_join = [
  "10.0.0.11",
  "10.0.0.12",
  "10.0.0.13",
]
# Prepare the data directory
sudo mkdir -p /opt/consul
sudo chown -R consul:consul /opt/consul
sudo chmod 750 /opt/consul
 
# Start the service
sudo systemctl enable --now consul
sudo systemctl status consul

Development Mode

# Start a single agent with everything enabled
consul agent -dev
 
# The UI is on http://localhost:8500
consul members
consul services

Security Configuration

TLS

# Generate a CA and a server certificate
consul tls ca create
consul tls cert create -server -dc=dc1
verify_incoming        = true
verify_outgoing        = true
verify_server_hostname = true
ca_file   = "consul-agent-ca.pem"
cert_file = "dc1-server-consul-0.pem"
key_file  = "dc1-server-consul-0-key.pem"

ACLs

# Bootstrap the ACL system — this prints the initial management token
consul acl bootstrap
 
# Store the token so the agent can use it
echo "CONSUL_HTTP_TOKEN=..." | sudo tee /etc/consul.d/consul.env
 
# Create a policy for a service
consul acl policy create -name "web-policy" -rules @web-policy.hcl
# web-policy.hcl
service_prefix "web" {
  policy = "write"
}
 
node_prefix "" {
  policy = "read"
}
# Create a token for the service
consul acl token create -description "web service" -policy-name web-policy

Service Registration

Create /etc/consul.d/web-service.hcl:

service {
  name = "web"
  port = 8080
 
  tags = ["prod", "http"]
 
  check {
    http = "http://localhost:8080/health"
    interval = "10s"
    timeout  = "2s"
  }
}
# Reload the agent configuration
consul reload
 
# Check that the service is registered and healthy
consul services
consul health checks service web

Service Intentions

Service intentions define who may talk to whom. They are the heart of the mesh security model:

# /etc/consul.d/intentions.hcl
Kind = "service-intentions"
Name = "web"
Sources = [
  {
    Name = "api"
    Action = "allow"
  },
  {
    Name = "*"
    Action = "deny"
  }
]
# Write the config entry
consul config write /etc/consul.d/intentions.hcl
 
# List the intentions
consul config list -kind service-intentions

Service Mesh (Consul Connect)

# Enable Connect on a service
consul config write -kind service-defaults -name web <<EOF
{
  "Protocol": "http",
  "Kind": "service-defaults",
  "Name": "web"
}
EOF
 
# Start a sidecar proxy for a service instance
consul connect proxy -sidecar-for web
 
# Check the proxy status
consul connect proxy-status

Upgrading

# 1. Back up the data directory
sudo systemctl stop consul
sudo tar -czf /var/backups/consul-$(date +%F).tar.gz -C /opt/consul data
sudo systemctl start consul
 
# 2. Upgrade the package
sudo apt update && sudo apt install --only-upgrade consul
 
# 3. Restart one server at a time and check the cluster
sudo systemctl restart consul
consul members
consul leader

Troubleshooting

Best Practices

  1. Run at least three servers spread across failure domains, plus the clients.
  2. Enable ACLs with default_policy = "deny" and issue scoped tokens per service.
  3. Always use TLS for gossip and RPC, and rotate the certificates.
  4. Define service intentions explicitly — they are your mesh authorisation policy.
  5. Use service-intentions config entries, not the deprecated legacy intention API.
  6. Register services with meaningful health checks so discovery only returns healthy instances.
  7. Upgrade one server at a time and check consul members in between.
  8. Back up the data directory before every upgrade.
  9. Use the Helm chart on Kubernetes with the official Consul image.

Next Steps