AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

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.

Ansible Rolling Update Debian/Ubuntu: apt Module Guide (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

code with ❤️ in GitHub

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 Ansibleswitching users with Ansible become

Category: installation

Watch the video: Ansible Rolling Update Debian/Ubuntu: apt Module Guide (Examples) — Video Tutorial

Browse all Ansible tutorials · AnsiblePilot Home