Ansible Rolling Update Debian/Ubuntu: apt Module Guide (Examples)
By Luca Berton · Published 2024-01-01 · Category: installation
How to perform rolling updates on Debian and Ubuntu with Ansible apt module. Update packages, handle reboots, and manage serial deployments with examples.

How to perform Rolling Update with Ansible in Debian-like systems?
I'm going to show you a live Playbook and some simple Ansible code. I'm Luca Berton and welcome to today's episode of Ansible Pilot
See also: Install Docker in Debian-like systems - Ansible module apt_key, apt_repository and apt
Ansible Rolling Update packages in Debian-like systems
Today we're talking about rolling updates on Debian-like systems using Ansible module apt.
We already talked about this module for installing packages but we would like to consider another use case.
This module allows you to manage packages with the apt package manager.
Parameters
• name _string_ • state _string_ • update_cache _boolean_ • upgrade _no/safe/full/dist_The parameter list is pretty wide but today we are focus on these four options for our use case.
The "name" parameter could be a package or we could select all the packages of the system with the "\*" star symbol.
The state for this case needs to be "latest" so we target the latest version for every package.
The "update_cache" is useful to forces the update of repository metadata before the installation.
Another useful option is "upgrade" with four alternatives: • default is a no, • if safe, performs an aptitude safe-upgrade, • if full, performs an aptitude full-upgrade, • if dist performs an apt-get dist-upgrade.
See also: Install Google Chrome in Debian like systems - Ansible module apt_key, apt_repository and apt
Demo
Let's jump in a real-life Playbook of Rolling Update on Debian-like systems with Ansible Playbook. • apt-nginx.yml
---
- name: rolling update Playbook
hosts: all
become: true
tasks:
- name: ensure pkg updated
ansible.builtin.apt:
name: nginx
state: latest
update_cache: true
• apt-system.yml
---
- name: rolling update Playbook
hosts: all
become: true
tasks:
- name: ensure system updated
ansible.builtin.apt:
name: "*"
state: latest
update_cache: true
Conclusion
Now you know better how to troubleshoot the most common Ansible error about privilege escalation.
See also: Install PostgreSQL in Debian-like systems - Ansible modules apt, stat, shell, service
Complete Rolling Update Playbook for Debian/Ubuntu
---
- name: Rolling update for Debian/Ubuntu servers
hosts: webservers
serial: 1
max_fail_percentage: 0
become: true
pre_tasks:
- name: Remove from load balancer
ansible.builtin.uri:
url: "http://lb.example.com/api/servers/{{ inventory_hostname }}/disable"
method: POST
delegate_to: localhost
- name: Wait for connections to drain
ansible.builtin.pause:
seconds: 30
tasks:
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 0
- name: Upgrade all packages
ansible.builtin.apt:
upgrade: dist
register: update_result
- name: Show update summary
ansible.builtin.debug:
msg: "Packages updated: {{ update_result.stdout_lines | select('match', '^Inst') | list | length }}"
when: update_result.stdout_lines is defined
- name: Check if reboot is required
ansible.builtin.stat:
path: /var/run/reboot-required
register: reboot_required
- name: Reboot if needed
ansible.builtin.reboot:
reboot_timeout: 300
msg: "Rebooting after package updates"
when: reboot_required.stat.exists
- name: Remove unused dependencies
ansible.builtin.apt:
autoremove: true
autoclean: true
post_tasks:
- name: Verify application health
ansible.builtin.uri:
url: "http://{{ inventory_hostname }}:8080/health"
status_code: 200
register: health
retries: 5
delay: 10
until: health.status == 200
- name: Re-enable in load balancer
ansible.builtin.uri:
url: "http://lb.example.com/api/servers/{{ inventory_hostname }}/enable"
method: POST
delegate_to: localhost
Security-Only Updates
- name: Install security updates only (Ubuntu/Debian)
ansible.builtin.apt:
upgrade: dist
update_cache: true
default_release: "{{ ansible_distribution_release }}-security"
become: true
Alternative using unattended-upgrades:
- name: Run unattended security upgrades
ansible.builtin.command: unattended-upgrade -v
become: true
register: unattended_result
changed_when: "'Packages that will be upgraded' in unattended_result.stdout"
Hold Packages (Prevent Upgrades)
- name: Hold critical packages
ansible.builtin.dpkg_selections:
name: "{{ item }}"
selection: hold
loop:
- postgresql-16
- docker-ce
become: true
Upgrade Type Comparison
| upgrade value | apt equivalent | Description |
|-----------------|---------------|-------------|
| dist | apt dist-upgrade | Smart upgrade, handles dependencies |
| full | apt full-upgrade | Same as dist (alias) |
| safe | apt upgrade | Conservative, won't remove packages |
| yes | apt upgrade | Same as safe |
FAQ
How do I check what will be updated without applying?
- name: Dry run — show pending updates
ansible.builtin.command: apt list --upgradable
register: pending
changed_when: false
- name: Show pending updates
ansible.builtin.debug:
var: pending.stdout_lines
How do I handle "dpkg lock" during updates?
- name: Wait for dpkg lock
ansible.builtin.shell: |
while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do
sleep 5
done
changed_when: false
become: true
What's /var/run/reboot-required?
Ubuntu/Debian create this file when a kernel or critical library update requires a reboot. Checking it is the standard way to determine if a reboot is needed after updates.
Update All Packages
- name: Update all packages
ansible.builtin.apt:
upgrade: dist
update_cache: true
cache_valid_time: 3600
become: true
Rolling Update Strategy
---
- name: Rolling update Debian servers
hosts: webservers
serial: 1 # One at a time
become: true
tasks:
- name: Remove from load balancer
delegate_to: localhost
uri:
url: "http://lb.internal/api/disable/{{ inventory_hostname }}"
method: POST
- name: Update apt cache
apt: update_cache=true
- name: Upgrade all packages
apt: upgrade=dist
register: apt_result
- name: Check if reboot required
stat: path=/var/run/reboot-required
register: reboot_required
- name: Reboot if needed
reboot:
reboot_timeout: 300
post_reboot_delay: 30
when: reboot_required.stat.exists
- name: Wait for service health
uri:
url: "http://{{ ansible_host }}/health"
status_code: 200
register: health
retries: 10
delay: 15
until: health.status == 200
- name: Re-enable in load balancer
delegate_to: localhost
uri:
url: "http://lb.internal/api/enable/{{ inventory_hostname }}"
method: POST
Security Updates Only
- name: Install security updates only
ansible.builtin.apt:
upgrade: dist
update_cache: true
default_release: "{{ ansible_distribution_release }}-security"
become: true
Install Specific Packages
- apt:
name:
- nginx=1.24.*
- python3
- python3-pip
state: present
update_cache: true
become: true
Autoremove and Clean
- name: Remove unused dependencies
apt:
autoremove: true
autoclean: true
become: true
Hold Package Version
# Prevent package from being upgraded
- ansible.builtin.dpkg_selections:
name: nginx
selection: hold
become: true
# Release hold
- dpkg_selections:
name: nginx
selection: install
Batch Updates with Reporting
- apt:
upgrade: dist
update_cache: true
register: update_result
become: true
- debug:
msg: "Updated {{ update_result.stdout_lines | select('match', '^Inst ') | list | length }} packages"
when: update_result.changed
apt Module Parameters
| Parameter | Description |
|-----------|-------------|
| name | Package name(s) |
| state | present, absent, latest, fixed |
| update_cache | Run apt update first |
| cache_valid_time | Skip update if recent (seconds) |
| upgrade | yes, safe, full, dist |
| autoremove | Remove unused deps |
| deb | Install from .deb file |
| default_release | Target release |
| force_apt_get | Use apt-get instead of aptitude |
FAQ
upgrade: safe vs dist vs full?
•safe: Only upgrades that don't remove packages
• dist: Handles changing dependencies (recommended)
• full: Like dist but more aggressive
How do I handle kernel updates that need reboots?
Check /var/run/reboot-required after updates and reboot with the reboot module in a rolling fashion.
Can I roll back an update?
apt doesn't have built-in rollback. Use snapshots (LVM/VM) before updates, or pin specific versions.
Related Articles
• Nginx vhost provisioning with Ansible • switching users with Ansible becomeCategory: installation
Watch the video: Ansible Rolling Update Debian/Ubuntu: apt Module Guide (Examples) — Video Tutorial