From Pings to Power: Managing UPS with Ansible and Semaphore

Ansible is at its best when you want consistent, repeatable infrastructure changes without installing agents everywhere. You describe the desired state in playbooks, and Ansible handles the rest over SSH. That makes it a great fit for homelabs: mixed hardware, small teams (often just you), and a constant need to keep services working after a rebuild.

Below is the exact style of workflow I built in my own homelab: back up NUT (Network UPS Tools) configs on Raspberry Pi hosts, keep credentials separated, and restore everything with a clean playbook. Then I run the same playbooks through Ansible Semaphore so I can trigger changes from a simple UI.

Why Ansible for a UPS stack

NUT setups are easy to get working once, but tedious to rebuild later. You need:

  • a consistent NUT configuration on each Pi
  • clean backups that do not leak credentials
  • a repeatable restore flow for new hardware
  • a way to run playbooks locally and from a scheduler/GUI

Ansible gives you all of that, with playbooks you can version control.

My homelab example: NUT + PeaNUT

I keep my inventory grouped by host types (rpi, lxcs, proxmox). For the UPS stack:

  • rpi3b and rpi4b run NUT
  • PeaNUT runs as a Docker container in an LXC host (sysdocker)
  • NUT secrets are stored in group vault vars, not in backups

Key pieces:

  • playbooks/nut-backup.yml backs up NUT configs and sanitizes secrets
  • playbooks/nut-restore.yml installs/configures NUT from templates
  • PeaNUT compose/settings/.env are backed up from /opt/stacks/peanut
  • PeaNUT settings are templated with NUT server placeholders so they align with the RPi configs during restore

Local testing workflow

I always dry-run first:

1ansible -i inventory/hosts.yml rpi -m ping
2ansible-playbook -i inventory/hosts.yml playbooks/nut-backup.yml --limit rpi --check

Then the real run:

1ansible-playbook -i inventory/hosts.yml playbooks/nut-backup.yml --limit rpi

That playbook:

  • fetches non-secret NUT config files
  • slurps upsmon.conf and upsd.users, then writes sanitized versions locally
  • backs up PeaNUT compose.yaml, settings.yml, and .env from sysdocker

Restoring NUT (and PeaNUT) cleanly

Restore is the reverse: install NUT on the Pis and deploy templates. PeaNUT is rebuilt from templates with credentials coming from vault files.

1ansible-playbook -i inventory/hosts.yml playbooks/nut-restore.yml --limit rpi,sysdocker --check
2ansible-playbook -i inventory/hosts.yml playbooks/nut-restore.yml --limit rpi,sysdocker

Because PeaNUT’s settings.yml is a template, the NUT server list is derived from inventory variables instead of hardcoded IPs or passwords. That keeps the UI in sync with the actual NUT configuration on each Pi.

Running the same playbooks in Semaphore

Semaphore adds a clean UI and scheduled runs without changing your playbooks. My typical setup:

  1. Create a new Project in Semaphore.
  2. Add the Git repo as a Repository.
  3. Add your Inventory (inventory/hosts.yml).
  4. Add credentials (SSH key + Ansible Vault password).
  5. Create a Template that runs playbooks/nut-backup.yml or playbooks/nut-restore.yml.
  6. Execute on demand or set a schedule for backups.

The benefit is simple: the playbooks stay the same, but you can run them from anywhere.

Takeaways

  • Ansible turns fragile, manual NUT setups into repeatable automation.
  • Sanitized backups keep you safe even if you share logs or commit artifacts.
  • Vault files keep secrets out of repo history and backups.
  • Semaphore is a convenient front-end to run the exact same playbooks on demand.

If you’re building a homelab UPS stack, this is a great first automation project: clear inputs, critical outputs, and a big payoff when you need to rebuild quickly.

comments powered by Disqus

Translations: