Consul is a distributed service mesh and service discovery tool from HashiCorp that enables services to find each other by name rather than by hard-coded IP addresses and ports. On RHEL 8, Consul is available through the official HashiCorp dnf repository and integrates with systemd for process management. This tutorial covers installing Consul in server mode on a single node, configuring the agent with a persistent data directory and built-in UI, registering a service with a health check, and querying the Consul DNS interface for service discovery. These skills form the foundation for multi-node Consul clusters used in production microservices environments.

Prerequisites

  • RHEL 8 server with root or sudo access
  • Internet access to reach rpm.releases.hashicorp.com
  • firewalld running and managing the active zone
  • bind-utils installed for DNS testing: dnf install -y bind-utils
  • A running nginx (or other web service) to register as an example service

Step 1 — Add the HashiCorp Repository and Install Consul

HashiCorp publishes an official RHEL/CentOS repository. Add it and install the consul package:

# Add the HashiCorp GPG key and repo
dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo

# Install Consul
dnf install -y consul

# Verify installation
consul version

Step 2 — Configure the Consul Agent

Create the primary configuration file. The example below runs Consul in single-node server mode with the built-in web UI enabled:

# Create the configuration directory if it does not exist
mkdir -p /etc/consul.d
mkdir -p /opt/consul/data

cat > /etc/consul.d/consul.hcl << 'EOF'
datacenter = "dc1"
data_dir   = "/opt/consul/data"

# Enable the built-in web UI (accessible on port 8500)
ui_config {
  enabled = true
}

# Run as a server node
server           = true
bootstrap_expect = 1

# Bind to the primary interface IP — replace with your server's LAN IP
bind_addr = "0.0.0.0"

# Address clients use to reach this agent
client_addr = "0.0.0.0"

# Cluster member advertise address — replace with your LAN IP
advertise_addr = "YOUR_SERVER_LAN_IP"

# Log level
log_level = "INFO"
EOF

# Set correct ownership (consul user is created during install)
chown -R consul:consul /etc/consul.d /opt/consul

Step 3 — Start and Enable the Consul Service

Consul ships with a systemd unit file. Enable it and verify the agent starts successfully:

systemctl enable --now consul
systemctl status consul

# Follow the Consul log to confirm leader election completes
journalctl -u consul -f --no-pager &
sleep 5
kill %1

# List cluster members (should show one server node)
consul members

# Check the leader
consul operator raft list-peers

You should see a line indicating the local node has been elected as cluster leader. The web UI is accessible at http://YOUR_SERVER_LAN_IP:8500.

Step 4 — Open Required Firewall Ports

Consul uses several ports. For a single-node development setup you need at minimum the HTTP API (8500) and DNS (8600):

# HTTP API and UI
firewall-cmd --permanent --add-port=8500/tcp
# DNS interface
firewall-cmd --permanent --add-port=8600/tcp
firewall-cmd --permanent --add-port=8600/udp
# RPC and Serf ports (required for multi-node clusters)
firewall-cmd --permanent --add-port=8300/tcp
firewall-cmd --permanent --add-port=8301/tcp
firewall-cmd --permanent --add-port=8301/udp
firewall-cmd --permanent --add-port=8302/tcp
firewall-cmd --permanent --add-port=8302/udp

firewall-cmd --reload
firewall-cmd --list-ports

Step 5 — Register a Service with a Health Check

Consul discovers services through service definition files placed in the configuration directory. Create a definition for nginx with an HTTP health check:

cat > /etc/consul.d/nginx.json << 'EOF'
{
  "service": {
    "name": "nginx",
    "tags": ["web", "http"],
    "port": 80,
    "check": {
      "id":       "nginx-http",
      "name":     "HTTP GET on port 80",
      "http":     "http://localhost:80/",
      "interval": "10s",
      "timeout":  "3s"
    }
  }
}
EOF

chown consul:consul /etc/consul.d/nginx.json

# Reload Consul to pick up the new service definition (no restart needed)
consul reload

# Verify the service was registered and its health check is passing
consul catalog services
consul health checks nginx

Step 6 — Query Services via the Consul DNS Interface

Consul exposes a DNS server on port 8600. Services are reachable at <service-name>.service.consul. Use dig to verify the DNS interface is resolving the nginx service:

# Query the Consul DNS server directly for the nginx service
dig @127.0.0.1 -p 8600 nginx.service.consul

# Query for SRV records (returns host + port)
dig @127.0.0.1 -p 8600 nginx.service.consul SRV

# Filter by tag using the ..service.consul format
dig @127.0.0.1 -p 8600 web.nginx.service.consul

# Use the Consul HTTP API to list service endpoints
curl -s http://127.0.0.1:8500/v1/catalog/service/nginx | python3 -m json.tool

# List all registered services via the API
curl -s http://127.0.0.1:8500/v1/catalog/services | python3 -m json.tool

A healthy nginx service returns an A record pointing to the agent’s advertise address, and the SRV record includes the registered port (80). If the health check is failing, the DNS query returns no results — Consul only routes DNS to healthy instances.

Conclusion

You have installed Consul on RHEL 8 using the official HashiCorp repository, configured a single-node server agent with a persistent data directory and web UI, registered nginx as a service with an HTTP health check, and queried the Consul DNS interface to confirm that healthy services are resolvable by name. The Consul DNS interface eliminates hard-coded IP addresses from your service configurations and automatically removes unhealthy instances from rotation. From here you can expand to a three-node cluster for high availability, enable TLS for agent-to-agent communication, and integrate Consul Connect for mutual TLS service mesh capabilities.

Next steps: Setting Up a Three-Node Consul Cluster on RHEL 8, Enabling Consul Connect Service Mesh with Envoy on RHEL 8, and Storing Application Secrets in Consul KV with ACL Policies.