Set up Consul Template to automatically generate configuration files from Consul key-value pairs and service discovery data. This tutorial covers installation, template creation, systemd service configuration, and monitoring setup for production environments.
Prerequisites
- Root or sudo access
- Consul server running
- Basic understanding of systemd services
What this solves
Consul Template watches Consul for service discovery and key-value changes, then renders configuration files dynamically. This eliminates manual configuration updates when services are added, removed, or moved, making your infrastructure more resilient and reducing operational overhead.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you get the latest versions.
sudo apt update && sudo apt upgrade -y
Install required packages
Install curl, unzip and other dependencies needed for Consul Template installation.
sudo apt install -y curl unzip jq
Download and install Consul Template
Download the latest Consul Template binary from HashiCorp's releases and install it to the system path.
export CONSUL_TEMPLATE_VERSION="0.37.5"
wget https://releases.hashicorp.com/consul-template/${CONSUL_TEMPLATE_VERSION}/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip
unzip consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip
sudo mv consul-template /usr/local/bin/
sudo chmod +x /usr/local/bin/consul-template
Create consul-template user
Create a dedicated system user for running Consul Template with minimal privileges.
sudo useradd --system --home /etc/consul-template --shell /bin/false consul-template
Create directory structure
Set up the required directories for configuration files, templates, and generated output.
sudo mkdir -p /etc/consul-template/templates
sudo mkdir -p /etc/consul-template/config
sudo mkdir -p /var/lib/consul-template
sudo mkdir -p /var/log/consul-template
sudo chown -R consul-template:consul-template /etc/consul-template
sudo chown -R consul-template:consul-template /var/lib/consul-template
sudo chown -R consul-template:consul-template /var/log/consul-template
Configure main Consul Template settings
Create the main configuration file that defines how Consul Template connects to Consul and processes templates.
consul {
address = "127.0.0.1:8500"
retry {
enabled = true
attempts = 12
backoff = "250ms"
max_backoff = "1m"
}
}
reload_signal = "SIGHUP"
kill_signal = "SIGINT"
max_stale = "10m"
log_level = "INFO"
log_file = "/var/log/consul-template/consul-template.log"
pid_file = "/var/lib/consul-template/consul-template.pid"
wait {
min = "5s"
max = "10s"
}
Create your first template
Create a simple template that generates a configuration file based on Consul services. This example creates an upstream configuration for NGINX.
{{- range services }}
{{- if .Tags | contains "web" }}
upstream {{ .Name }} {
{{- range service .Name }}
server {{ .Address }}:{{ .Port }};
{{- end }}
}
{{- end }}
{{- end }}
Generated at {{ timestamp }}
Services found: {{ services | len }}
Configure template processing
Add template configuration to the main config file to specify input template, output file, and commands to run when the template changes.
template {
source = "/etc/consul-template/templates/nginx-upstream.conf.tpl"
destination = "/etc/nginx/conf.d/upstream.conf"
create_dest_dirs = true
command = "nginx -t && systemctl reload nginx"
command_timeout = "30s"
error_on_missing_key = false
perms = 0644
backup = true
left_delimiter = "{{"
right_delimiter = "}}"
wait {
min = "2s"
max = "10s"
}
}
Create systemd service file
Set up systemd service management for Consul Template with proper restart policies and security settings.
[Unit]
Description=Consul Template
Documentation=https://github.com/hashicorp/consul-template
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/consul-template/config/consul-template.hcl
[Service]
Type=notify
User=consul-template
Group=consul-template
ExecStart=/usr/local/bin/consul-template -config-dir=/etc/consul-template/config
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
TimeoutStopSec=30
Security settings
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectHome=yes
ProtectSystem=strict
ReadWritePaths=/var/lib/consul-template /var/log/consul-template /etc/nginx/conf.d
[Install]
WantedBy=multi-user.target
Set correct permissions
Ensure all files have the correct ownership and permissions for the consul-template user.
sudo chown -R consul-template:consul-template /etc/consul-template
sudo chmod 755 /etc/consul-template
sudo chmod 644 /etc/consul-template/config/*
sudo chmod 644 /etc/consul-template/templates/*
Configure log rotation
Set up log rotation to prevent Consul Template logs from filling up disk space.
/var/log/consul-template/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
copytruncate
create 644 consul-template consul-template
}
Enable and start the service
Enable Consul Template to start automatically on boot and start the service now.
sudo systemctl daemon-reload
sudo systemctl enable consul-template
sudo systemctl start consul-template
Create dynamic configuration templates
Create HAProxy backend template
This template generates HAProxy backend configurations dynamically based on Consul service registrations.
global
daemon
maxconn 4096
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
{{- range services }}
{{- if .Tags | contains "backend" }}
backend {{ .Name }}
balance roundrobin
{{- range service .Name }}
server {{ .Name }}-{{ .ID }} {{ .Address }}:{{ .Port }} check
{{- end }}
{{- end }}
{{- end }}
frontend web
bind *:80
{{- range services }}
{{- if .Tags | contains "backend" }}
use_backend {{ .Name }} if { hdr(host) -i {{ .Name }}.example.com }
{{- end }}
{{- end }}
Add HAProxy template to configuration
Configure Consul Template to process the HAProxy template and reload HAProxy when changes occur.
template {
source = "/etc/consul-template/templates/haproxy-backends.cfg.tpl"
destination = "/etc/haproxy/haproxy.cfg"
create_dest_dirs = true
command = "haproxy -f /etc/haproxy/haproxy.cfg -c && systemctl reload haproxy"
command_timeout = "30s"
perms = 0644
backup = true
}
Create application configuration template
Generate application configuration files using Consul key-value store data.
{
"database": {
"host": "{{ keyOrDefault "config/database/host" "localhost" }}",
"port": {{ keyOrDefault "config/database/port" "5432" }},
"username": "{{ keyOrDefault "config/database/username" "app" }}",
"password": "{{ keyOrDefault "config/database/password" "changeme" }}",
"name": "{{ keyOrDefault "config/database/name" "myapp" }}"
},
"redis": {
"host": "{{ keyOrDefault "config/redis/host" "localhost" }}",
"port": {{ keyOrDefault "config/redis/port" "6379" }}
},
"services": {
{{- range $index, $service := services }}
{{- if $index }},{{- end }}
"{{ .Name }}": [
{{- range $sIndex, $instance := service .Name }}
{{- if $sIndex }},{{- end }}
{
"address": "{{ .Address }}",
"port": {{ .Port }},
"tags": [{{ range $tIndex, $tag := .Tags }}{{- if $tIndex }},{{- end }}"{{ . }}"{{ end }}]
}
{{- end }}
]
{{- end }}
},
"generated_at": "{{ timestamp }}"
}
Set up systemd integration
Configure service dependencies
Ensure Consul Template starts after Consul and required services are available. This setup provides coordination between Consul service discovery and template rendering.
[Unit]
After=consul.service
Wants=consul.service
[Service]
Environment=CONSUL_HTTP_ADDR=127.0.0.1:8500
Restart=always
RestartSec=10
sudo mkdir -p /etc/systemd/system/consul-template.service.d
Create health check script
Set up a health check script to monitor Consul Template status and template rendering.
#!/bin/bash
Check if Consul Template is running
if ! systemctl is-active --quiet consul-template; then
echo "ERROR: Consul Template service is not running"
exit 1
fi
Check if PID file exists and process is running
PID_FILE="/var/lib/consul-template/consul-template.pid"
if [[ -f "$PID_FILE" ]]; then
PID=$(cat "$PID_FILE")
if ! ps -p "$PID" > /dev/null 2>&1; then
echo "ERROR: Consul Template process not found (PID: $PID)"
exit 1
fi
else
echo "ERROR: PID file not found"
exit 1
fi
Check if log file has recent entries (within 5 minutes)
LOG_FILE="/var/log/consul-template/consul-template.log"
if [[ -f "$LOG_FILE" ]]; then
LAST_LOG=$(stat -c %Y "$LOG_FILE")
CURRENT_TIME=$(date +%s)
TIME_DIFF=$((CURRENT_TIME - LAST_LOG))
if [[ $TIME_DIFF -gt 300 ]]; then
echo "WARNING: No recent log entries (last update: ${TIME_DIFF}s ago)"
fi
fi
echo "OK: Consul Template is healthy"
exit 0
sudo chmod +x /usr/local/bin/consul-template-health-check
Configure systemd timer for health checks
Set up automated health monitoring using systemd timers.
[Unit]
Description=Consul Template Health Check
After=consul-template.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/consul-template-health-check
User=consul-template
Group=consul-template
[Unit]
Description=Run Consul Template health check every 2 minutes
Requires=consul-template-health.service
[Timer]
OnBootSec=5min
OnUnitActiveSec=2min
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable consul-template-health.timer
sudo systemctl start consul-template-health.timer
Configure template monitoring and alerting
Set up Prometheus metrics collection
Configure Consul Template to expose metrics for monitoring and create a metrics endpoint.
telemetry {
prometheus_retention_time = "30s"
disable_hostname = true
}
Create monitoring template
Generate a monitoring configuration that tracks template rendering and service health.
{
"consul_template": {
"version": "0.37.5",
"status": "running",
"last_update": "{{ timestamp }}",
"services_count": {{ services | len }},
"services": {
{{- range $index, $service := services }}
{{- if $index }},{{- end }}
"{{ .Name }}": {
"instances": {{ service .Name | len }},
"healthy_instances": {{ service .Name "passing" | len }},
"tags": [{{ range $tIndex, $tag := .Tags }}{{- if $tIndex }},{{- end }}"{{ . }}"{{ end }}]
}
{{- end }}
}
}
}
Configure alerting rules
Set up alerting based on template rendering failures and service availability. This integrates well with existing Prometheus alerting setups.
template {
source = "/etc/consul-template/templates/monitoring.json.tpl"
destination = "/var/lib/consul-template/monitoring.json"
create_dest_dirs = true
command = "curl -X POST http://localhost:9090/api/v1/admin/tsdb/delete_series?match[]=consul_template_info"
command_timeout = "10s"
perms = 0644
wait {
min = "10s"
max = "30s"
}
}
Create alert notification script
Set up a script that sends notifications when template rendering fails or services become unhealthy.
#!/bin/bash
ALERT_TYPE="${1:-unknown}"
SERVICE_NAME="${2:-unknown}"
MESSAGE="${3:-No message provided}"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
Log alert
echo "[$TIMESTAMP] ALERT: $ALERT_TYPE - $SERVICE_NAME - $MESSAGE" >> /var/log/consul-template/alerts.log
Send to syslog
logger -t consul-template "ALERT: $ALERT_TYPE - $SERVICE_NAME - $MESSAGE"
Optional: Send to external monitoring system
curl -X POST "https://hooks.slack.com/services/YOUR/WEBHOOK/URL" \
-H 'Content-type: application/json' \
--data "{\"text\":\"Consul Template Alert: $MESSAGE\"}"
exit 0
sudo chmod +x /usr/local/bin/consul-template-alert
Verify your setup
sudo systemctl status consul-template
consul-template -version
sudo journalctl -u consul-template -f --no-pager
ls -la /var/log/consul-template/
curl -s localhost:8500/v1/agent/services | jq .
Test template rendering
Verify that templates are being rendered correctly by checking generated files.
sudo cat /etc/nginx/conf.d/upstream.conf
sudo /usr/local/bin/consul-template-health-check
sudo systemctl status consul-template-health.timer
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Service fails to start | Consul not running | sudo systemctl status consul && systemctl start consul |
| Templates not rendering | Permission denied on output files | sudo chown consul-template:consul-template /path/to/output && chmod 644 /path/to/output |
| Command execution fails | Insufficient permissions for reload commands | Add consul-template user to required groups or use sudo rules |
| Template syntax errors | Invalid template syntax | consul-template -template="/path/to/template:/tmp/test" -once -dry |
| High CPU usage | Template rendering too frequently | Increase wait times in template configuration |
| Log files growing large | Verbose logging enabled | Change log_level to "WARN" or "ERROR" in configuration |
Next steps
- Configure Traefik with Consul service discovery for dynamic load balancing
- Implement Consul ACL security and encryption for production hardening
- Set up NGINX monitoring with Prometheus and Grafana for comprehensive observability
- Configure Consul Template with SSL encryption
- Set up Consul Template multi-datacenter replication
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Default configuration
CONSUL_TEMPLATE_VERSION="${CONSUL_TEMPLATE_VERSION:-0.37.5}"
CONSUL_ADDRESS="${CONSUL_ADDRESS:-127.0.0.1:8500}"
# Usage function
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -v VERSION Consul Template version (default: $CONSUL_TEMPLATE_VERSION)"
echo " -c ADDRESS Consul address (default: $CONSUL_ADDRESS)"
echo " -h Show this help"
exit 1
}
# Parse command line arguments
while getopts "v:c:h" opt; do
case $opt in
v) CONSUL_TEMPLATE_VERSION="$OPTARG" ;;
c) CONSUL_ADDRESS="$OPTARG" ;;
h) usage ;;
*) usage ;;
esac
done
# Logging functions
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Cleanup function for rollback
cleanup() {
log_error "Installation failed. Cleaning up..."
systemctl stop consul-template 2>/dev/null || true
systemctl disable consul-template 2>/dev/null || true
rm -f /etc/systemd/system/consul-template.service
rm -f /usr/local/bin/consul-template
userdel consul-template 2>/dev/null || true
rm -rf /etc/consul-template /var/lib/consul-template /var/log/consul-template
systemctl daemon-reload
}
# Set error trap
trap cleanup ERR
# Check if running as root
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root"
exit 1
fi
# Detect distribution
echo "[1/10] Detecting operating system..."
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update && apt upgrade -y"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
log_info "Detected: $PRETTY_NAME"
else
log_error "Cannot detect operating system"
exit 1
fi
# Update system packages
echo "[2/10] Updating system packages..."
$PKG_UPDATE
# Install required packages
echo "[3/10] Installing required packages..."
$PKG_INSTALL curl unzip jq
# Download and install Consul Template
echo "[4/10] Downloading Consul Template v${CONSUL_TEMPLATE_VERSION}..."
cd /tmp
wget -q "https://releases.hashicorp.com/consul-template/${CONSUL_TEMPLATE_VERSION}/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip"
unzip -q "consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip"
mv consul-template /usr/local/bin/
chmod 755 /usr/local/bin/consul-template
rm -f "consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip"
# Verify installation
/usr/local/bin/consul-template -version
log_info "Consul Template installed successfully"
# Create consul-template user
echo "[5/10] Creating consul-template user..."
if ! id consul-template >/dev/null 2>&1; then
useradd --system --home /etc/consul-template --shell /bin/false consul-template
log_info "Created consul-template user"
else
log_warn "User consul-template already exists"
fi
# Create directory structure
echo "[6/10] Creating directory structure..."
mkdir -p /etc/consul-template/{templates,config}
mkdir -p /var/lib/consul-template
mkdir -p /var/log/consul-template
# Set proper ownership and permissions
chown -R consul-template:consul-template /etc/consul-template
chown -R consul-template:consul-template /var/lib/consul-template
chown -R consul-template:consul-template /var/log/consul-template
chmod 755 /etc/consul-template/{templates,config}
chmod 755 /var/lib/consul-template
chmod 755 /var/log/consul-template
# Create main configuration file
echo "[7/10] Creating main configuration file..."
cat > /etc/consul-template/config/consul-template.hcl << EOF
consul {
address = "${CONSUL_ADDRESS}"
retry {
enabled = true
attempts = 12
backoff = "250ms"
max_backoff = "1m"
}
}
reload_signal = "SIGHUP"
kill_signal = "SIGINT"
max_stale = "10m"
log_level = "INFO"
log_file = "/var/log/consul-template/consul-template.log"
pid_file = "/var/lib/consul-template/consul-template.pid"
wait {
min = "5s"
max = "10s"
}
EOF
# Create sample template
echo "[8/10] Creating sample template..."
cat > /etc/consul-template/templates/nginx-upstream.conf.tpl << 'EOF'
{{- range services }}
{{- if .Tags | contains "web" }}
upstream {{ .Name }} {
{{- range service .Name }}
server {{ .Address }}:{{ .Port }};
{{- end }}
}
{{- end }}
{{- end }}
# Generated at {{ timestamp }}
# Services found: {{ services | len }}
EOF
# Set proper permissions for config files
chown consul-template:consul-template /etc/consul-template/config/consul-template.hcl
chown consul-template:consul-template /etc/consul-template/templates/nginx-upstream.conf.tpl
chmod 644 /etc/consul-template/config/consul-template.hcl
chmod 644 /etc/consul-template/templates/nginx-upstream.conf.tpl
# Create systemd service file
echo "[9/10] Creating systemd service..."
cat > /etc/systemd/system/consul-template.service << EOF
[Unit]
Description=Consul Template
Documentation=https://github.com/hashicorp/consul-template
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/consul-template/config/consul-template.hcl
[Service]
Type=exec
User=consul-template
Group=consul-template
ExecStart=/usr/local/bin/consul-template -config-dir=/etc/consul-template/config
ExecReload=/bin/kill -HUP \$MAINPID
KillMode=process
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/consul-template /var/lib/consul-template
CapabilityBoundingSet=
AmbientCapabilities=
[Install]
WantedBy=multi-user.target
EOF
chmod 644 /etc/systemd/system/consul-template.service
# Enable and start service
systemctl daemon-reload
systemctl enable consul-template
# Final verification
echo "[10/10] Performing verification checks..."
# Check if binary is executable and in PATH
if command -v consul-template >/dev/null 2>&1; then
log_info "Consul Template binary is accessible"
else
log_error "Consul Template binary not found in PATH"
exit 1
fi
# Check configuration syntax
if sudo -u consul-template /usr/local/bin/consul-template -config-dir=/etc/consul-template/config -dry-run >/dev/null 2>&1; then
log_info "Configuration syntax is valid"
else
log_warn "Configuration validation failed - this is normal if Consul is not running"
fi
# Check service status
if systemctl is-enabled consul-template >/dev/null 2>&1; then
log_info "Consul Template service is enabled"
else
log_error "Failed to enable Consul Template service"
exit 1
fi
# Disable error trap for successful completion
trap - ERR
log_info "Consul Template installation completed successfully!"
echo ""
echo "Next steps:"
echo "1. Configure your templates in /etc/consul-template/templates/"
echo "2. Add template blocks to /etc/consul-template/config/consul-template.hcl"
echo "3. Start the service: systemctl start consul-template"
echo "4. Check status: systemctl status consul-template"
echo "5. View logs: journalctl -u consul-template -f"
Review the script before running. Execute with: bash install.sh