Post
PT EN

Modernizing Legacy: Automating Static Applications (NGINX/HAProxy) with Consul-Template

Modernizing Legacy: Automating Static Applications (NGINX/HAProxy) with Consul-Template

Introduction

The promise of modern infrastructure is dynamism: services scale automatically, IPs change, and containers are orchestrated in real-time. Tools like HashiCorp Consul were created to manage this chaos through Service Discovery via DNS or API.

However, the reality of most companies includes a vast layer of “legacy infrastructure” or traditional technologies that weren’t designed for this ephemeral world. Web servers like NGINX, load balancers like HAProxy, or even old Java/PHP applications generally depend on static configuration files on disk.

How do you make an NGINX, which expects a fixed list of IPs in its nginx.conf file, automatically discover that new application containers came up in the cluster?

This article explores Consul-Template, an agnostic and powerful tool that acts as the bridge between Consul’s dynamism and the stability of static file-based software.

The Conflict: Dynamic vs. Static

Imagine a common Load Balancing scenario with NGINX. To balance load between three application servers, your upstream configuration looks like this:

1
2
3
4
5
upstream backend {
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

If the Auto Scaling Group adds a fourth server (10.0.0.4), NGINX won’t know about its existence. If server 10.0.0.1 dies, NGINX will continue sending traffic to it (causing 502 Bad Gateway errors) until someone edits the file and reloads the service.

Trying to solve this with cron scripts or fragile automation is a bad pattern. We need a solution that “watches” cluster state and reacts immediately.

The Solution: Consul-Template

consul-template is a standalone binary (daemon) created by HashiCorp. Its function is simple but vital: it queries data from Consul (Services, Key-Value Store, Vault Secrets), renders a configuration file based on a template, and optionally executes a reload command.

The workflow is continuous:

  1. Watch: The daemon monitors the Consul cluster in real-time.
  2. Change: When a change occurs (e.g., a new “backend” service is registered), it detects the event.
  3. Render: It rewrites the local configuration file (e.g., /etc/nginx/conf.d/default.conf) using the new data.
  4. Execute: It runs an arbitrary command to apply the change (e.g., nginx -s reload).

Practical Implementation

To automate the NGINX from the previous example, we stop editing the .conf file directly and start editing a template file (.ctmpl).

The syntax used is Go Template (the same used in Helm and Kubernetes).

The Template (nginx.conf.ctmpl)

Instead of fixed IPs, we use the range function to iterate over all healthy services registered in Consul:

1
2
3
4
5
6
7
8
9
10
11
12
upstream backend {
  {{ range service "backend-api" " }}
    server {{ .Address " }}:{{ .Port " }};
  {{ end " }}
}

server {
    listen 80;
    location / {
        proxy_pass http://backend;
    }
}

Execution

We start the consul-template process pointing to the template, final destination, and reload command:

1
2
consul-template \
  -template "/tmp/nginx.conf.ctmpl:/etc/nginx/conf.d/default.conf:nginx -s reload"

From this moment, automation is active.

  1. If 10 new backend-api containers come up, Consul detects it.
  2. consul-template receives the notification.
  3. It writes the 10 new server IP:PORT; lines in the NGINX file.
  4. It executes NGINX reload.

All this happens in milliseconds, without human intervention and without downtime (due to NGINX’s reload nature).

Going Beyond Service Discovery

The benefit of Consul-Template is that it doesn’t just serve IP lists. It can be used to inject Dynamic Configurations from Consul KV.

Imagine controlling NGINX behavior via Consul:

1
2
3
4
5
6
7
location / {
    {{ if key "config/nginx/maintenance_mode" | parseBool " }}
        return 503 "We are under maintenance";
    {{ else " }}
        proxy_pass http://backend;
    {{ end " }}
}

In this example, an operator can enable “Maintenance Mode” just by changing a key in Consul KV, and all NGINX servers in the fleet will be updated instantly.

Conclusion

Modernizing infrastructure doesn’t necessarily mean rewriting all applications to be cloud-native or replacing robust technologies like NGINX and HAProxy with complex Service Meshes.

Consul-Template acts as a modernization enabler. It allows “legacy” tools (based on static files) to gain dynamic superpowers, integrating perfectly with Auto Scaling and Container environments.

This approach offers the best of both worlds: the stability and performance of mature tools, with the agility and automation of a Service Discovery-based architecture.

This post is licensed under CC BY 4.0 by the author.