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 on Ubuntu 26.04 LTS: sudo-rs, APT Rollback, and Automating Resolute Raccoon

By Luca Berton · Published 2024-01-01 · Category: installation

Install and configure Ansible on Ubuntu 26.04 LTS Resolute Raccoon. Automate sudo-rs (Rust-based sudo), APT 3.2 transaction rollback, Wayland-only desktops.

Ubuntu 26.04 LTS Changes That Affect Your Ansible Playbooks

Ubuntu 26.04 LTS "Resolute Raccoon" ships changes that matter to anyone managing Linux infrastructure with Ansible. Most posts about this release will focus on desktop polish. This one focuses on what changes for automation engineers.

The headline: sudo-rs is now the default sudo provider. Your become: true tasks still work, but the binary handling privilege escalation on every managed host is fundamentally different. Here's what you need to know and how to automate it.

See also: Ansible on Ubuntu 26.04 LTS Automation Complete Guide

Install Ansible on Ubuntu 26.04

# Update package index
sudo apt update

# Install Ansible from Ubuntu repos
sudo apt install -y ansible

# Or install latest from PPA
sudo add-apt-repository --yes ppa:ansible/ansible
sudo apt update
sudo apt install -y ansible

# Or install via pip (recommended for control nodes)
sudo apt install -y python3-pip python3-venv
python3 -m venv ~/ansible-venv
source ~/ansible-venv/bin/activate
pip install ansible

# Verify
ansible --version

Automate Control Node Setup

---
- name: Set up Ansible control node on Ubuntu 26.04
  hosts: localhost
  become: true

  tasks:
    - name: Install Python and pip
      ansible.builtin.apt:
        name:
          - python3
          - python3-pip
          - python3-venv
          - sshpass
          - git
        state: present
        update_cache: true

    - name: Install Ansible in virtualenv
      ansible.builtin.pip:
        name:
          - ansible
          - ansible-lint
          - molecule
        virtualenv: /opt/ansible-venv
        virtualenv_command: python3 -m venv

    - name: Create symlink for ansible commands
      ansible.builtin.file:
        src: "/opt/ansible-venv/bin/{{ item }}"
        dest: "/usr/local/bin/{{ item }}"
        state: link
      loop:
        - ansible
        - ansible-playbook
        - ansible-galaxy
        - ansible-vault
        - ansible-lint

sudo-rs: The Change That Actually Matters

sudo is the binary that runs as root on every Linux machine you manage. It's been written in C since 1980. In the last few years it's had CVEs that allowed local privilege escalation — Baron Samedit (CVE-2021-3156) being the memorable one, with 10 years of unpatched exposure across most Linux distros.

sudo-rs is a full rewrite in Rust. Same /etc/sudoers config format. Same CLI interface. Drop-in replacement. The memory safety guarantees Rust provides matter specifically here — on the binary that handles privilege escalation.

This isn't experimental. sudo-rs passed a full security audit by Radically Open Security in 2023. The sudo-rs team (developed by Prossimo/ISRG, the people behind Let's Encrypt) worked directly with Todd C. Miller, the original sudo maintainer. Ubuntu 26.04 making it the default is the signal that it's production-ready.

What Changes for Ansible

The good news: nothing breaks. Ansible's become method calls sudo as a binary. sudo-rs provides the same binary interface. Your existing playbooks work without modification.

What you should validate:

- name: Verify sudo-rs on Ubuntu 26.04 hosts
  hosts: ubuntu_26_04
  become: true

  tasks:
    - name: Check which sudo binary is installed
      ansible.builtin.command:
        cmd: dpkg -l sudo-rs
      register: sudo_rs_check
      changed_when: false
      failed_when: false

    - name: Confirm sudo-rs is active
      ansible.builtin.command:
        cmd: sudo --version
      register: sudo_version
      changed_when: false
      become: false

    - name: Report sudo implementation
      ansible.builtin.debug:
        msg: >
          Host {{ inventory_hostname }} running:
          {{ sudo_version.stdout_lines[0] | default('unknown') }}
          {% if 'sudo-rs' in (sudo_version.stdout | default('')) %}
          (Rust implementation - memory safe)
          {% else %}
          (Traditional C implementation)
          {% endif %}

Enforce sudo-rs Across Your Fleet

If you're managing a mixed fleet of Ubuntu versions and want to standardize on sudo-rs where available:

- name: Enforce sudo-rs on supported Ubuntu hosts
  hosts: ubuntu
  become: true

  tasks:
    - name: Install sudo-rs on Ubuntu 24.04+
      ansible.builtin.apt:
        name: sudo-rs
        state: present
      when:
        - ansible_distribution == "Ubuntu"
        - ansible_distribution_version is version('24.04', '>=')
      register: sudo_rs_install

    - name: Verify sudo still works after switch
      ansible.builtin.command:
        cmd: sudo -n true
      become: false
      changed_when: false
      when: sudo_rs_install.changed

    - name: Validate sudoers syntax
      ansible.builtin.command:
        cmd: visudo -c
      changed_when: false

Harden sudoers Configuration

sudo-rs uses the same /etc/sudoers format, so your existing Ansible sudoers management works identically:

- name: Harden sudoers on Ubuntu 26.04
  hosts: ubuntu_26_04
  become: true

  tasks:
    - name: Configure secure sudoers defaults
      ansible.builtin.lineinfile:
        path: /etc/sudoers
        regexp: "^Defaults\\s+{{ item.key }}"
        line: "Defaults {{ item.key }}={{ item.value }}"
        validate: "visudo -cf %s"
      loop:
        - { key: "passwd_timeout", value: "1" }
        - { key: "timestamp_timeout", value: "5" }
        - { key: "logfile", value: '"/var/log/sudo.log"' }

    - name: Ensure Defaults use_pty is set
      ansible.builtin.lineinfile:
        path: /etc/sudoers
        regexp: "^Defaults\\s+use_pty"
        line: "Defaults use_pty"
        validate: "visudo -cf %s"

    - name: Create Ansible service account sudoers
      ansible.builtin.copy:
        content: |
          # Ansible automation account - managed by Ansible
          ansible ALL=(ALL) NOPASSWD: ALL
          Defaults:ansible !requiretty
        dest: /etc/sudoers.d/ansible
        mode: '0440'
        validate: "visudo -cf %s"

See also: Ansible on Ubuntu 20.04 LTS: Unattended Security Updates Complete Guide

APT 3.2: Transaction Rollback

APT 3.2 in Ubuntu 26.04 introduces a transaction log with full rollback support. apt history-rollback undoes any package operation. This changes how you handle failed deployments and package management with Ansible.

Automate APT Rollback

- name: Safe package deployment with APT rollback
  hosts: ubuntu_26_04
  become: true

  tasks:
    - name: Record APT transaction ID before changes
      ansible.builtin.command:
        cmd: apt history --format=json
      register: apt_history_before
      changed_when: false

    - name: Store last transaction ID
      ansible.builtin.set_fact:
        pre_deploy_transaction: "{{ (apt_history_before.stdout | from_json)[-1].id | default('0') }}"

    - name: Deploy application packages
      ansible.builtin.apt:
        name:
          - nginx
          - php8.4-fpm
          - php8.4-mysql
          - redis-server
        state: present
        update_cache: true
      register: pkg_install

    - name: Validate application health
      ansible.builtin.uri:
        url: "http://localhost/health"
        return_content: true
        status_code: 200
      register: health_check
      failed_when: false
      when: pkg_install.changed

    - name: Rollback if health check fails
      ansible.builtin.command:
        cmd: "apt history-rollback -y {{ pre_deploy_transaction | int + 1 }}"
      when:
        - pkg_install.changed
        - health_check.status is defined
        - health_check.status != 200
      register: rollback_result

    - name: Alert on rollback
      ansible.builtin.debug:
        msg: "ROLLBACK EXECUTED on {{ inventory_hostname }} - deployment failed health check"
      when: rollback_result is changed

APT Rollback for Kernel Updates

- name: Safe kernel update with rollback capability
  hosts: ubuntu_26_04
  become: true

  tasks:
    - name: Record current kernel
      ansible.builtin.command:
        cmd: uname -r
      register: kernel_before
      changed_when: false

    - name: Apply kernel update
      ansible.builtin.apt:
        name: linux-generic
        state: latest
        update_cache: true
      register: kernel_update

    - name: Schedule rollback check after reboot
      ansible.builtin.copy:
        content: |
          #!/bin/bash
          # Auto-rollback if system doesn't come up healthy
          sleep 300
          if ! curl -sf http://localhost/health > /dev/null 2>&1; then
            apt history-rollback -y last
            reboot
          fi
        dest: /usr/local/bin/kernel-rollback-check.sh
        mode: '0755'
      when: kernel_update.changed

    - name: Reboot with watchdog
      ansible.builtin.reboot:
        reboot_timeout: 600
        test_command: uptime
      when: kernel_update.changed

Kernel 7.0: What Matters for Automation

Ubuntu 26.04 ships Linux Kernel 7.0 with Intel TDX (Trust Domain Extensions) for confidential computing and broader hardware support.

- name: Validate and configure Kernel 7.0 features
  hosts: ubuntu_26_04
  become: true

  tasks:
    - name: Check kernel version
      ansible.builtin.command:
        cmd: uname -r
      register: kernel_version
      changed_when: false

    - name: Verify kernel is 7.x
      ansible.builtin.assert:
        that: kernel_version.stdout is version('7.0', '>=')
        fail_msg: "Expected kernel 7.x, got {{ kernel_version.stdout }}"

    - name: Check Intel TDX support
      ansible.builtin.command:
        cmd: grep -c tdx /proc/cpuinfo
      register: tdx_check
      changed_when: false
      failed_when: false

    - name: Enable TDX if supported
      ansible.builtin.copy:
        content: |
          # Intel TDX confidential computing
          options kvm_intel tdx=1
        dest: /etc/modprobe.d/tdx.conf
        mode: '0644'
      when: tdx_check.stdout | int > 0
      notify: update initramfs

    - name: Configure kernel parameters for production
      ansible.posix.sysctl:
        name: "{{ item.key }}"
        value: "{{ item.value }}"
        sysctl_set: true
        reload: true
      loop:
        - { key: "vm.swappiness", value: "10" }
        - { key: "net.core.somaxconn", value: "65535" }
        - { key: "net.ipv4.tcp_max_syn_backlog", value: "65535" }
        - { key: "fs.file-max", value: "2097152" }
        - { key: "kernel.pid_max", value: "4194304" }

  handlers:
    - name: update initramfs
      ansible.builtin.command:
        cmd: update-initramfs -u

See also: Ansible on Ubuntu 22.04 LTS: Unattended Security Updates Complete Guide

Wayland-Only: X11 Is Gone

Ubuntu 26.04 removes X11 entirely. No more Xorg fallback session in GDM. If your Ansible playbooks install or configure X11 packages, they'll fail.

- name: Configure Wayland desktop on Ubuntu 26.04
  hosts: ubuntu_26_04_desktop
  become: true

  tasks:
    - name: Remove any X11 references from old playbooks
      ansible.builtin.apt:
        name:
          - xorg
          - xserver-xorg
          - xinit
        state: absent
      failed_when: false

    - name: Install Wayland desktop components
      ansible.builtin.apt:
        name:
          - ubuntu-desktop
          - mutter
          - xdg-desktop-portal-gnome
          - xwayland  # For legacy X11 apps that need compatibility
        state: present

    - name: Configure GDM for Wayland
      ansible.builtin.copy:
        content: |
          [daemon]
          WaylandEnable=true
          AutomaticLoginEnable=false

          [security]

          [xdmcp]

          [chooser]

          [debug]
        dest: /etc/gdm3/custom.conf
        mode: '0644'
      notify: restart gdm

    - name: Set environment for Wayland
      ansible.builtin.copy:
        content: |
          # Wayland environment variables
          MOZ_ENABLE_WAYLAND=1
          QT_QPA_PLATFORM=wayland
          GDK_BACKEND=wayland
          ELECTRON_OZONE_PLATFORM_HINT=wayland
        dest: /etc/environment.d/wayland.conf
        mode: '0644'

  handlers:
    - name: restart gdm
      ansible.builtin.systemd:
        name: gdm3
        state: restarted

Update Display Testing in CI

If your Ansible molecule tests or CI pipelines use virtual displays:

# Old approach (broken on 26.04)
# - name: Start Xvfb
#   command: Xvfb :99 -screen 0 1024x768x24

# New approach for Wayland
- name: Install headless Wayland compositor for testing
  ansible.builtin.apt:
    name: weston
    state: present

- name: Start headless Wayland for testing
  ansible.builtin.shell: |
    weston --backend=headless --width=1024 --height=768 &
    export WAYLAND_DISPLAY=wayland-0
  async: 3600
  poll: 0

ROCm: AMD GPU Compute as a One-Liner

Ubuntu 26.04 includes ROCm in the official repositories. No more third-party repo configuration.

- name: Install AMD ROCm GPU compute
  hosts: gpu_workers
  become: true

  tasks:
    - name: Install ROCm from official repos
      ansible.builtin.apt:
        name:
          - rocm
        state: present
        update_cache: true
      when: ansible_distribution_version is version('26.04', '>=')

    - name: Add user to render and video groups
      ansible.builtin.user:
        name: "{{ item }}"
        groups:
          - render
          - video
        append: true
      loop: "{{ gpu_users }}"

    - name: Verify ROCm installation
      ansible.builtin.command:
        cmd: rocm-smi
      register: rocm_status
      changed_when: false

    - name: Display GPU info
      ansible.builtin.debug:
        msg: "{{ rocm_status.stdout }}"

ROCm vs Pre-26.04 Comparison

# Before Ubuntu 26.04 (manual repo setup)
# - name: Add ROCm GPG key
#   apt_key:
#     url: https://repo.radeon.com/rocm/rocm.gpg.key
# - name: Add ROCm repository
#   apt_repository:
#     repo: "deb [arch=amd64] https://repo.radeon.com/rocm/apt/6.0 jammy main"
# - name: Install ROCm
#   apt:
#     name: rocm-hip-sdk

# Ubuntu 26.04 - it's in the default repos
- name: Install ROCm
  ansible.builtin.apt:
    name: rocm
    state: present

Ptyxis Terminal

Ubuntu 26.04 replaces GNOME Terminal with Ptyxis — GPU-accelerated, GTK4-native, with tabs that hold state. If your playbooks install or configure gnome-terminal, update them:

- name: Configure terminal on Ubuntu 26.04
  hosts: ubuntu_26_04_desktop
  become: true

  tasks:
    - name: Ensure Ptyxis is installed (default on 26.04)
      ansible.builtin.apt:
        name: ptyxis
        state: present

    - name: Remove old gnome-terminal references
      ansible.builtin.apt:
        name: gnome-terminal
        state: absent
      failed_when: false

Complete Ubuntu 26.04 Base Image Playbook

For building hardened base images, sudo-rs and the APT rollback flow are the two things to validate first:

---
- name: Ubuntu 26.04 LTS hardened base image
  hosts: ubuntu_26_04
  become: true

  tasks:
    # 1. Validate sudo-rs is active
    - name: Confirm sudo-rs is the default
      ansible.builtin.shell:
        cmd: "sudo --version 2>&1 | head -1"
      register: sudo_impl
      changed_when: false
      become: false

    - name: Assert sudo-rs
      ansible.builtin.assert:
        that: "'sudo-rs' in sudo_impl.stdout or 'rustc' in sudo_impl.stdout"
        fail_msg: "sudo-rs is not the default sudo on this host"

    # 2. Full system update
    - name: Update all packages
      ansible.builtin.apt:
        upgrade: dist
        update_cache: true
        cache_valid_time: 3600

    # 3. Install baseline packages
    - name: Install standard tooling
      ansible.builtin.apt:
        name:
          - curl
          - wget
          - git
          - vim
          - htop
          - unattended-upgrades
          - apt-listchanges
          - needrestart
          - ufw
          - fail2ban
          - chrony
          - jq
        state: present

    # 4. Configure unattended security updates
    - name: Enable automatic security updates
      ansible.builtin.copy:
        content: |
          APT::Periodic::Update-Package-Lists "1";
          APT::Periodic::Unattended-Upgrade "1";
          APT::Periodic::AutocleanInterval "7";
        dest: /etc/apt/apt.conf.d/20auto-upgrades
        mode: '0644'

    # 5. Harden SSH
    - name: Harden SSH configuration
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "^{{ item.key }}"
        line: "{{ item.key }} {{ item.value }}"
        validate: "sshd -t -f %s"
      loop:
        - { key: "PermitRootLogin", value: "no" }
        - { key: "PasswordAuthentication", value: "no" }
        - { key: "X11Forwarding", value: "no" }
        - { key: "MaxAuthTries", value: "3" }
        - { key: "ClientAliveInterval", value: "300" }
        - { key: "ClientAliveCountMax", value: "2" }
      notify: restart sshd

    # 6. Configure UFW firewall
    - name: Enable UFW with default deny
      community.general.ufw:
        state: enabled
        default: deny
        direction: incoming

    - name: Allow SSH
      community.general.ufw:
        rule: allow
        port: '22'
        proto: tcp

    # 7. Configure fail2ban
    - name: Configure fail2ban for SSH
      ansible.builtin.copy:
        content: |
          [sshd]
          enabled = true
          port = ssh
          filter = sshd
          logpath = /var/log/auth.log
          maxretry = 3
          bantime = 3600
          findtime = 600
        dest: /etc/fail2ban/jail.d/sshd.conf
        mode: '0644'
      notify: restart fail2ban

    # 8. Configure chrony NTP
    - name: Configure NTP
      ansible.builtin.template:
        src: chrony.conf.j2
        dest: /etc/chrony/chrony.conf
        mode: '0644'
      notify: restart chrony

    # 9. Kernel hardening
    - name: Apply kernel hardening parameters
      ansible.posix.sysctl:
        name: "{{ item.key }}"
        value: "{{ item.value }}"
        sysctl_set: true
        reload: true
      loop:
        - { key: "net.ipv4.conf.all.rp_filter", value: "1" }
        - { key: "net.ipv4.conf.default.rp_filter", value: "1" }
        - { key: "net.ipv4.icmp_echo_ignore_broadcasts", value: "1" }
        - { key: "net.ipv4.conf.all.accept_redirects", value: "0" }
        - { key: "net.ipv6.conf.all.accept_redirects", value: "0" }
        - { key: "net.ipv4.conf.all.send_redirects", value: "0" }
        - { key: "kernel.randomize_va_space", value: "2" }
        - { key: "kernel.kptr_restrict", value: "2" }

    # 10. Cleanup
    - name: Remove unnecessary packages
      ansible.builtin.apt:
        autoremove: true
        purge: true

  handlers:
    - name: restart sshd
      ansible.builtin.systemd:
        name: ssh
        state: restarted

    - name: restart fail2ban
      ansible.builtin.systemd:
        name: fail2ban
        state: restarted

    - name: restart chrony
      ansible.builtin.systemd:
        name: chrony
        state: restarted

Ubuntu 26.04 Quick Reference

FeatureWhat ChangedAnsible Impact
sudo-rsRust-based sudo is defaultbecome: true works unchanged; validate on fleet
APT 3.2Transaction rollback via apt history-rollbackNew rollback capability for safe deployments
Kernel 7.0Intel TDX, broader hardwareNew sysctl params, TDX module options
Wayland-onlyX11 completely removedRemove Xorg tasks; update CI virtual displays
ROCm in reposapt install rocmRemove manual repo configuration tasks
PtyxisReplaces GNOME TerminalUpdate desktop provisioning playbooks
LTS until 20315 years standard, 10 with ProLong-term automation target

FAQ

Does Ansible's become method work with sudo-rs?

Yes. Ansible calls sudo as a binary, and sudo-rs is a drop-in replacement with the same CLI interface. No changes to become: true, become_method: sudo, or ansible_become_password are needed. Your existing playbooks work without modification.

Should I switch existing Ubuntu 24.04 hosts to sudo-rs?

sudo-rs is available as a package on Ubuntu 24.04 and later. For security-sensitive environments, it's worth the switch. Test your sudoers configuration first — sudo-rs intentionally doesn't implement some rarely-used sudoers features that are common sources of security issues.

How do I use APT rollback in Ansible error handling?

Use Ansible's block/rescue pattern combined with apt history-rollback. Record the transaction ID before your package block, then call rollback in the rescue section if any task fails. See the safe deployment playbook above for the full pattern.

Will my X11-dependent Ansible roles break on Ubuntu 26.04?

If your roles install xorg, xserver-xorg, or configure X11 settings, they'll fail or produce warnings on Ubuntu 26.04. Audit roles for X11 dependencies and create Wayland alternatives. The xwayland package provides compatibility for legacy X11 applications that run under Wayland.

Is Ubuntu 26.04 safe for production Ansible managed nodes?

Yes. As an LTS release with 5 years of standard support (10 with Ubuntu Pro until 2036), Ubuntu 26.04 is designed for production. The sudo-rs change actually improves security posture. Test your playbooks in staging first, particularly around any X11 or custom sudo configurations.

Conclusion

Ubuntu 26.04 LTS "Resolute Raccoon" brings meaningful changes for automation engineers. sudo-rs eliminates an entire class of privilege escalation vulnerabilities in the most critical binary on your systems. APT 3.2 rollback gives you a safety net for package operations. The Wayland-only shift requires updating desktop provisioning playbooks. And ROCm in default repos simplifies GPU compute automation from a multi-step process to a single apt install. Validate sudo-rs and APT rollback first — they're the changes that impact every host you manage.

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home