Ansible - KeepAliveD
Refreshed June 2026: module names are now FQCN, the handler uses
ansible.builtin.service, and the health check in the template is wired in with atrack_scriptblock (the original defined avrrp_scriptbut never referenced it, so it never actually ran). The example check also moved off Quagga, which is end of life and superseded by FRRouting, to HAProxy. I trimmed the oversized kitchen-sinkgroup_varsfrom the original down to just the KeepAliveD variables so the post stays on topic. Rendered output below is from ansible-core 2.19.
Background
The goal is to dynamically generate a KeepAliveD failover configuration across two Ubuntu nodes acting as routers and load balancers, so a single VIP floats between them with VRRP. KeepAliveD is still the standard tool for this, so the approach holds up; only the Ansible syntax around it needed a cleanup.
Tenant Variables
A fictitious tenant defined in a vars file. Trimmed to what the KeepAliveD config actually uses:
---
tenant_name: tenant_1
tenant_vips:
- 10.10.10.100
- 10.10.10.101
- 10.10.10.102
- 10.10.10.103
- 10.10.10.104
The Playbook
Template out keepalived.conf and notify a handler to restart the service:
---
- hosts: all
vars_files:
- ../vars/tenant_1.yml
tasks:
- name: tenant_configs | config | setting up keepalived vips
ansible.builtin.template:
src: ../templates/etc/keepalived/keepalived.conf.j2
dest: /etc/keepalived/keepalived.conf
owner: root
group: root
mode: "0644"
run_once: true
notify: restart keepalived
handlers:
- name: restart keepalived
ansible.builtin.service:
name: keepalived
state: restarted
The Template
The template builds the VRRP instance from the variables. The vrrp_script defines a health check, and the track_script block inside the instance is what actually activates it. In the original this wiring was missing, so the check was defined but never used. Here the check watches HAProxy, the service the VIP fronts.
## {{ ansible_managed }}
vrrp_script chk_haproxy {
script "/usr/bin/killall -0 haproxy" # verify the process is alive
interval 2 # check every 2 seconds
weight 2 # add 2 points of priority if OK
}
vrrp_instance tenant_1 {
state MASTER
interface {{ keepalived_vip_int }}
virtual_router_id {{ keepalived_router_id }}
priority {{ keepalived_router_pri }}
advert_int 1
virtual_ipaddress {
{{ keepalived_vip }}
}
virtual_ipaddress_excluded {
{% for vip in tenant_vips %}
{{ vip }}
{% endfor %}
}
track_script {
chk_haproxy
}
notify_master {{ notify_master_script }}
notify_backup {{ notify_backup_script }}
}
Rendered Output
With the KeepAliveD variables below applied, the template renders to:
## Managed by Ansible
vrrp_script chk_haproxy {
script "/usr/bin/killall -0 haproxy" # verify the process is alive
interval 2 # check every 2 seconds
weight 2 # add 2 points of priority if OK
}
vrrp_instance tenant_1 {
state MASTER
interface eth0
virtual_router_id 23
priority 101
advert_int 1
virtual_ipaddress {
10.10.10.4
}
virtual_ipaddress_excluded {
10.10.10.100
10.10.10.101
10.10.10.102
10.10.10.103
10.10.10.104
}
track_script {
chk_haproxy
}
notify_master /opt/scripts/master.sh
notify_backup /opt/scripts/backup.sh
}
The KeepAliveD Variables
The variables that drive the template. The per-node priority and virtual_router_id normally live in host_vars so each node differs:
keepalived_vip: 10.10.10.4 # on the same network as the primary interface
keepalived_vip_int: "{{ pri_bind_interface }}" # the interface the VIP binds to
keepalived_router_id: 23 # unique per VRRP domain on a subnet
keepalived_router_pri: 101 # per-node; higher wins MASTER (host_vars)
notify_master_script: /opt/scripts/master.sh
notify_backup_script: /opt/scripts/backup.sh
notify_fault_script: /opt/scripts/fault.sh
What Else Has Changed Since 2015
- Quagga is end of life. The original tied the health check to Quagga’s
zebra. If you still run a software router on these nodes, it is FRRouting now, which also ships azebradaemon, so the samekillall -0pattern works if you point it there. - Networking moved to netplan. On modern Ubuntu the
/etc/network/interfacesandinterfaces.dfiles the original leaned on are replaced by netplan. The KeepAliveD config itself is unaffected. - KeepAliveD itself is unchanged in shape. VRRP instances, VIPs, and tracked scripts work the same, which is why this post needed a tune-up rather than a rewrite.
Conclusion
KeepAliveD is still how you float a VIP between two nodes. Template the config from your tenant data, wire the health check in with track_script, and let a handler restart the service on change.
Enjoy!
Comments