This episode brings Keepalived configuration into the CI/CD pipeline: validating keepalived.conf in CI with keepalived -t, automated deployment from versioned configuration, and preflight checks that test failover readiness before changes release to production.

Configuration changed directly on production servers is the root of many incidents. Episode 19 puts an end to that habit: every keepalived.conf change must pass through a pipeline — validated in CI, deployed automatically, and tested with preflight checks before being declared fit.
This approach isn't about tools, it's about discipline. With CI validating syntax and templates, accidental configuration differences between nodes can be rejected before they touch a server. By the end of this episode, releasing Keepalived configuration is as rigorous as releasing application code.
The first step is making sure every change passes keepalived -t. This can be run inside a container:
name: validate-keepalived-config
on:
pull_request:
paths:
- "roles/keepalived/**"
jobs:
syntax:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Render template
run: |
ansible localhost -m template \
-a "src=roles/keepalived/templates/keepalived.conf.j2 dest=/tmp/keepalived.conf"
- name: Validate syntax
run: |
docker run --rm -v /tmp/keepalived.conf:/etc/keepalived/keepalived.conf:ro \
osixia/keepalived:2.2.7 -t -f /etc/keepalived/keepalived.confThe validate-keepalived-config workflow runs whenever configuration files change. The template is rendered first with Ansible, then osixia/keepalived:2.2.7 -t validates the syntax in a container. Pull requests with broken syntax are automatically rejected.
Validating a single node isn't enough — the template must be valid for every variable combination. Render the template for each host and validate them all in one job. That way, errors like a mistyped variable for a specific node are caught early.
Deploy to staging first, then production:
name: deploy-keepalived
on:
push:
branches: [main]
jobs:
staging:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy ke staging
run: ansible-playbook -i inventory/staging/hosts.yml roles/keepalived/playbook.yml
production:
needs: staging
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy ke produksi
run: ansible-playbook -i inventory/prod/hosts.yml roles/keepalived/playbook.ymlThe staging job runs the playbook against the staging environment; the production job only runs after staging succeeds. This chain ensures configuration is proven in staging before touching production.
Because Keepalived 2.x supports reload via SIGHUP, deploying doesn't need to drop the VIP. The playbook uses state: reloaded as in episode 11, so changes apply without disturbing running traffic.
Before marking a release as done, run preflight checks that measure failover readiness:
sudo keepalived -t -f /etc/keepalived/keepalived.conf
sudo systemctl is-active keepalived
ping -c 1 -W 1 192.168.1.100 > /dev/null && echo "VIP OK"
sudo ipvsadm -L -n > /dev/null && echo "IPVS OK"The sequence keepalived -t, systemctl is-active, the VIP check, and ipvsadm -L -n verifies layer by layer: configuration, daemon, VIP, and the LVS table. Everything must be green before the release is declared successful.
For a deeper preflight, perform a measured failover in staging:
systemctl stop keepalived
sleep 3
ping -c 1 -W 1 192.168.1.100 > /dev/null && echo "FAILOVER OK"
systemctl start keepalivedThe systemctl stop keepalived then checking that the VIP moved to another node proves failover works. Add this step to the staging pipeline so every configuration change also genuinely tests failover readiness.
Configuration is code: ask for reviews. Other collaborators can check whether virtual_router_id is consistent, whether priorities make sense, and whether there are unintended changes. CI catches machine errors; review catches decision errors.
After a successful release, tag the configuration version so rollback is easy:
git tag -a config-$(date +%Y%m%d-%H%M) -m "rls keepalived"
git push origin --tagsThe git tag -a config-$(date +%Y%m%d-%H%M) command creates a release timestamp marker that can be rolled back with a single git checkout.
Episode 19 closes the modern configuration management loop: validation in CI before a pull request merges, staged deployment to staging then production, and preflight checks that test failover readiness. Keepalived configuration is now treated as a release artifact, not a file edited casually.
Key takeaways:
keepalived -t container.In episode 20 next, we cover monitoring and alerting — observing Keepalived status with a Prometheus exporter and logs, alerting for state changes, lost VIPs, and unhealthy backends, plus operational dashboards for HA status.