DEV Community

Cover image for Automating Consul with Ansible: Infrastructure DNS for Devs
Athreya aka Maneshwar
Athreya aka Maneshwar

Posted on

Automating Consul with Ansible: Infrastructure DNS for Devs

Hi there! I'm Maneshwar. Right now, I’m building LiveAPI, a first-of-its-kind tool that helps you automatically index API endpoints across all your repositories. LiveAPI makes it easier to discover, understand, and interact with APIs in large infrastructures.

Continuing our Ansible journey, let’s wire up Consul — HashiCorp’s service mesh and internal DNS provider — using clean Ansible roles.

We’ll install it using HashiCorp’s apt repository, configure it in a role-driven fashion, and deploy Consul agents as either servers or clients using tags. Let's get to it.

The Playbook

Your consul.yml playbook defines which hosts should run the role and how we want to tag their responsibilities:

- name: Install and configure Consul hosts: all become: yes roles: - consul tags: - master - client 
Enter fullscreen mode Exit fullscreen mode

This gives us the flexibility to run only server or client setup by tagging the playbook execution later.

🗂 Folder Layout

Here’s how the consul role is structured:

ansible-galaxy init roles/consul --offline

roles/consul/ ├── defaults/main.yml ├── handlers/main.yml ├── tasks/ │ ├── install.yml │ ├── configure.yml │ └── main.yml ├── templates/ │ ├── client_consul.hcl.j2 │ ├── master_consul.hcl.j2 │ └── master_server.hcl.j2 ├── vars/main.yml 
Enter fullscreen mode Exit fullscreen mode

Installing Consul the Right Way

Use the official HashiCorp GPG key and apt repo setup (the new secure way):

tasks/install.yml

- name: Add HashiCorp GPG key (new method) ansible.builtin.shell: | curl -fsSL https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /etc/apt/keyrings/hashicorp-archive-keyring.gpg > /dev/null args: creates: /etc/apt/keyrings/hashicorp-archive-keyring.gpg - name: Add HashiCorp repository (new method) ansible.builtin.shell: | echo "deb [signed-by=/etc/apt/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list args: creates: /etc/apt/sources.list.d/hashicorp.list - name: Run apt update ansible.builtin.shell: apt update - name: Install Consul ansible.builtin.apt: name: consul state: present update_cache: no 
Enter fullscreen mode Exit fullscreen mode

⚠️ You can replace the shell tasks with ansible.builtin.get_url and apt_repository for a cleaner, idempotent install — but this approach is closer to the manual HashiCorp documentation and works well for quick setups.

Templates: Server & Client Configs

Define separate configuration templates for master/server and clients.

templates/master_consul.hcl.j2

node_name = "consul-{{ inventory_hostname }}-server" server = true bootstrap = true datacenter = "dc1" data_dir = "consul/data" log_level = "INFO" bind_addr = "0.0.0.0" client_addr = "0.0.0.0" advertise_addr = "{{ internal_ip }}" ui_config { enabled = true } connect { enabled = true } dns_config { enable_truncate = true } 
Enter fullscreen mode Exit fullscreen mode

Use this for Consul servers that need to bootstrap the cluster and expose the UI.

templates/master_server.hcl.j2

log_level = "DEBUG" server = true bootstrap_expect = 1 advertise_addr = "{{ internal_ip }}" connect { enabled = true } ui_config { enabled = true } 
Enter fullscreen mode Exit fullscreen mode

This can act as an additional config layer — or be merged into the main server template if needed.

templates/client_consul.hcl.j2

node_name = "consul-{{ inventory_hostname }}-client" server = false datacenter = "dc1" data_dir = "/opt/consul" log_level = "INFO" bind_addr = "{{ internal_ip }}" advertise_addr = "{{ internal_ip }}" retry_join = ["{{ groups['nomadclientandservers'] | map('extract', hostvars, 'internal_ip') | list | first }}"] dns_config { enable_truncate = true } service { id = "dns" name = "dns" tags = ["primary"] address = "localhost" port = 8600 check { id = "dns" name = "Consul DNS TCP on port 8600" tcp = "localhost:8600" interval = "10s" timeout = "1s" } } 
Enter fullscreen mode Exit fullscreen mode

This config sets up DNS forwarding and a basic health check for clients. Ensure internal_ip is defined in your host vars.

Configuring Consul with Ansible

Now, wire it all together in the configure.yml:

tasks/configure.yml

- name: Create consul data directory ansible.builtin.file: path: /opt/consul state: directory owner: consul group: consul mode: 0755 tags: [master, client] - name: Set proper permissions for consul directories ansible.builtin.file: path: "{{ item }}" state: directory owner: consul group: consul recurse: yes loop: - /opt/consul - /etc/consul.d tags: [master, client] - name: Deploy master Consul config ansible.builtin.template: src: master_consul.hcl.j2 dest: /etc/consul.d/consul.hcl owner: consul group: consul mode: 0644 when: "'master' in ansible_run_tags" tags: master - name: Deploy master server config ansible.builtin.template: src: master_server.hcl.j2 dest: /etc/consul.d/server.hcl owner: consul group: consul mode: 0644 when: "'master' in ansible_run_tags" tags: master - name: Deploy client Consul config ansible.builtin.template: src: client_consul.hcl.j2 dest: /etc/consul.d/consul.hcl owner: consul group: consul mode: 0644 when: "'client' in ansible_run_tags" tags: client - name: Restart consul service ansible.builtin.systemd: name: consul state: restarted tags: [master, client] 
Enter fullscreen mode Exit fullscreen mode

Running the Playbook

Split your deployment by tags:

ansible-playbook consul.yml --tags master -v ansible-playbook consul.yml --tags client -v 
Enter fullscreen mode Exit fullscreen mode

This gives you full control over when to deploy servers vs clients.

What You Achieved

  • Installed Consul using HashiCorp’s secure apt setup
  • Bootstrapped a Consul server with the UI enabled
  • Deployed DNS-aware clients with health checks
  • Used tags to keep roles clean and reusable

This approach brings modularity, repeatability, and clean separation to your infra automation.

🧠 Bonus: Let LiveAPI Handle Your APIs

LiveAPI helps you get all your backend APIs documented in a few minutes.

With LiveAPI, you can generate interactive API docs that allow users to search and execute endpoints directly from the browser.

LiveAPI Demo

If you're tired of updating Swagger manually or syncing Postman collections, give it a shot.

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.