- Fixed 4 broken markdown links (bad relative paths in See Also sections) - Corrected n8n port binding to 127.0.0.1:5678 (matches actual deployment) - Updated SnapRAID article with actual majorhome paths (/majorRAID, disk1-3) - Converted 67 Obsidian wikilinks to relative markdown links or plain text - Added YAML frontmatter to 35 articles missing it entirely - Completed frontmatter on 8 articles with missing fields Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
95 lines
3.2 KiB
Markdown
95 lines
3.2 KiB
Markdown
---
|
|
title: Standardizing unattended-upgrades Across Ubuntu Fleet with Ansible
|
|
domain: selfhosting
|
|
category: security
|
|
tags:
|
|
- ansible
|
|
- ubuntu
|
|
- apt
|
|
- unattended-upgrades
|
|
- fleet-management
|
|
status: published
|
|
created: '2026-03-16'
|
|
updated: '2026-03-16'
|
|
---
|
|
|
|
# Standardizing unattended-upgrades Across Ubuntu Fleet with Ansible
|
|
|
|
When some Ubuntu hosts in a fleet self-update via `unattended-upgrades` and others don't, they drift apart over time — different kernel versions, different reboot states, inconsistent behavior. This article covers how to diagnose the drift and enforce uniform auto-update config across all Ubuntu hosts using Ansible.
|
|
|
|
## Diagnosing the Problem
|
|
|
|
If only some Ubuntu hosts are flagging for reboot, check:
|
|
|
|
```bash
|
|
# What triggered the reboot flag?
|
|
cat /var/run/reboot-required.pkgs
|
|
|
|
# Is unattended-upgrades installed and active?
|
|
systemctl status unattended-upgrades
|
|
cat /etc/apt/apt.conf.d/20auto-upgrades
|
|
|
|
# When did apt last run?
|
|
ls -lt /var/log/apt/history.log*
|
|
```
|
|
|
|
The reboot flag is written to `/var/run/reboot-required` by `update-notifier-common` when packages like the kernel, glibc, or systemd are updated. If some hosts have `unattended-upgrades` running and others don't, the ones that self-updated will flag for reboot while the others lag behind.
|
|
|
|
## The Fix — Ansible Playbook
|
|
|
|
Add these tasks to your update playbook **before** the apt cache update step:
|
|
|
|
```yaml
|
|
- name: Ensure unattended-upgrades is installed on Ubuntu servers
|
|
ansible.builtin.apt:
|
|
name:
|
|
- unattended-upgrades
|
|
- update-notifier-common
|
|
state: present
|
|
update_cache: true
|
|
when: ansible_facts['os_family'] == "Debian"
|
|
|
|
- name: Enforce uniform auto-update config on Ubuntu servers
|
|
ansible.builtin.copy:
|
|
dest: /etc/apt/apt.conf.d/20auto-upgrades
|
|
content: |
|
|
APT::Periodic::Update-Package-Lists "1";
|
|
APT::Periodic::Unattended-Upgrade "1";
|
|
owner: root
|
|
group: root
|
|
mode: '0644'
|
|
when: ansible_facts['os_family'] == "Debian"
|
|
|
|
- name: Ensure unattended-upgrades service is enabled and running
|
|
ansible.builtin.systemd:
|
|
name: unattended-upgrades
|
|
enabled: true
|
|
state: started
|
|
when: ansible_facts['os_family'] == "Debian"
|
|
```
|
|
|
|
Running this across the `ubuntu` group ensures every host has the same config on every Ansible run — idempotent and safe.
|
|
|
|
## Rebooting Flagged Hosts
|
|
|
|
Once identified, reboot specific hosts without touching the rest:
|
|
|
|
```bash
|
|
# Reboot just the flagging hosts
|
|
ansible-playbook reboot.yml -l teelia,tttpod
|
|
|
|
# Run full update on remaining hosts to bring them up to the same kernel
|
|
ansible-playbook update.yml -l dca,majorlinux,majortoot
|
|
```
|
|
|
|
## Notes
|
|
|
|
- `unattended-upgrades` runs daily on its own schedule — hosts that haven't checked yet will lag behind but catch up within 24 hours
|
|
- The other hosts showing `ok` (not `changed`) on the config tasks means they were already correctly configured
|
|
- After a kernel update is pulled, only an actual reboot clears the `/var/run/reboot-required` flag — Ansible reporting the flag is informational only
|
|
|
|
## See Also
|
|
|
|
- [Ansible Getting Started](../../01-linux/shell-scripting/ansible-getting-started.md)
|
|
- [Linux Server Hardening Checklist](linux-server-hardening-checklist.md)
|