consul-template lets you turn static configuration files into dynamic ones, updating them automatically whenever upstream configuration changes.

Let’s say you have a web application that needs to connect to a database. Instead of hardcoding the database host and port in your app’s config file, you can use consul-template to pull that information from Consul’s Key/Value store and inject it into your config file.

Here’s a simplified example. Imagine your application expects a database.conf file like this:

[database]
host = 192.168.1.100
port = 5432

You can achieve this with consul-template using a template file. First, store your dynamic values in Consul’s KV store:

consul kv put services/webapp/database/host "192.168.1.100"
consul kv put services/webapp/database/port "5432"

Now, create a consul-template template file, let’s call it database.conf.ctmpl:

[database]

host = {{ key "services/webapp/database/host" }}


port = {{ key "services/webapp/database/port" }}

When consul-template runs, it will query Consul for the specified keys and render database.conf like this:

[database]
host = 192.168.1.100
port = 5432

The real magic happens when you change the values in Consul. If you update the database host to 192.168.1.101:

consul kv put services/webapp/database/host "192.168.1.101"

consul-template, watching for changes, will re-render database.conf automatically.

The core problem consul-template solves is managing configuration drift in distributed systems. As services scale up and down, their addresses and parameters change. Manually updating configuration files across many instances is error-prone and time-consuming. consul-template centralizes configuration management in Consul’s KV store and propagates changes automatically.

Internally, consul-template works by:

  1. Watching Consul: It continuously monitors Consul for changes to the keys you’ve specified in your template files.
  2. Fetching Data: When a change is detected, it fetches the latest values for those keys from Consul’s KV store.
  3. Rendering Templates: It uses the Go text/template or html/template package to process your .ctmpl files, substituting the fetched values into the template.
  4. Writing Output: The rendered content is written to a specified output file.
  5. Executing Commands: Optionally, after writing the new configuration file, consul-template can execute a command (e.g., to signal a service to reload its configuration).

The primary lever you control is the structure of your .ctmpl files and the Consul KV paths you reference. You can use standard template functions like if, range, join, and toUpper to build complex configurations. For instance, to generate a comma-separated list of backend server IPs from a Consul service’s health checks:

Suppose you have multiple instances of a "backend-service" registered in Consul. You can get their addresses like this:

consul-template -template "backends.conf.ctmpl:backends.conf" \
  -consul-addr "127.0.0.1:8500" \
  -exec "echo 'Configuration updated, reloading service...' && systemctl reload myapp.service"

And your backends.conf.ctmpl might look like:

backends = [

{{ range $index, $service := services "backend-service" }}


  {{ $address := service $service.ID | first_field }}


  {{ $port := service $service.ID | second_field }}


  "{{ $address }}:{{ $port }}"{{ if not $index }},{{ end }}


{{ end }}

]

Here, services "backend-service" lists all service registrations tagged with "backend-service". service $service.ID fetches details for a specific service instance, and first_field and second_field are custom functions (or you’d parse the output) to extract the IP and port from Consul’s service address format.

The exec flag is crucial for integrating with your application’s lifecycle. Without it, consul-template would just update the file, and your application wouldn’t know to re-read it. By specifying a command like systemctl reload myapp.service, you trigger a graceful configuration reload.

A common pitfall is assuming consul-template will automatically restart your application. It won’t. The exec command is your hook to tell your application how to react to a config change. This might be a SIGHUP signal, a systemctl reload, or even a full restart depending on your application’s capabilities.

The next step is integrating consul-template with service discovery, where the template dynamically generates configurations based on the healthy instances of a service registered in Consul.

Want structured learning?

Take the full Consul course →