Introduction

The ansible.builtin.yum and ansible.builtin.dnf modules manage packages on Red Hat family systems — RHEL, CentOS, CentOS Stream, Fedora, Oracle Linux, Rocky Linux, and AlmaLinux. They handle installation, updates, removal, group installs, and repository management in idempotent tasks.

yum vs dnf — Which Module to Use?

OSNative Package ManagerRecommended Module
RHEL/CentOS 7yumansible.builtin.yum
RHEL 8/9dnfansible.builtin.dnf
CentOS Stream 8/9dnfansible.builtin.dnf
Fedoradnfansible.builtin.dnf
Rocky/Alma Linuxdnfansible.builtin.dnf

Cross-platform: ansible.builtin.package auto-detects the right module.

Module Parameters

ParameterTypeDescription
namelist/stringPackage name(s) to manage
statestringpresent, latest, absent, installed, removed
update_cacheboolRefresh repo metadata before install
enablerepostringEnable specific repo for this transaction
disablerepostringDisable specific repo for this transaction
excludestringPackages to exclude
skip_brokenboolSkip packages with dependency issues
allow_downgradeboolAllow installing older version
securityboolOnly install security updates
bugfixboolOnly install bugfix updates
lock_timeoutintSeconds to wait for lock (dnf only)
install_weak_depsboolInstall weak dependencies (dnf only)

Basic Operations

Install a Package

- name: Install wget
  ansible.builtin.yum:
    name: wget
    state: present

Install Multiple Packages

- name: Install common tools
  ansible.builtin.yum:
    name:
      - wget
      - curl
      - vim
      - git
      - htop
      - tree
    state: present

Install a Specific Version

- name: Install specific wget version
  ansible.builtin.yum:
    name: wget-1.19.5-7.el8
    state: present
    allow_downgrade: true

Update a Package

- name: Update nginx to latest
  ansible.builtin.yum:
    name: nginx
    state: latest

Update All Packages

- name: Update all packages
  ansible.builtin.yum:
    name: "*"
    state: latest
    security: true  # Only security updates

Remove a Package

- name: Remove old package
  ansible.builtin.yum:
    name: httpd
    state: absent
    autoremove: true  # Remove unused dependencies

Install from Local RPM or URL

# Install from URL
- name: Install Chrome from URL
  ansible.builtin.yum:
    name: https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
    state: present
    disable_gpg_check: true

# Install from local RPM
- name: Install local package
  ansible.builtin.yum:
    name: /tmp/mypackage-1.0.rpm
    state: present

Repository Management

Enable/Disable Repos for a Transaction

- name: Install from EPEL
  ansible.builtin.yum:
    name: htop
    state: present
    enablerepo: epel

- name: Install without testing repo
  ansible.builtin.yum:
    name: nginx
    state: present
    disablerepo: "testing*"

Add a Repository

- name: Add EPEL repository
  ansible.builtin.yum_repository:
    name: epel
    description: EPEL YUM repo
    baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/
    gpgcheck: true
    gpgkey: https://download.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-{{ ansible_distribution_major_version }}
    enabled: true

Refresh Cache

- name: Install with fresh cache
  ansible.builtin.yum:
    name: nginx
    state: present
    update_cache: true

Package Groups

# Install a package group
- name: Install Development Tools
  ansible.builtin.yum:
    name: "@Development Tools"
    state: present

# DNF module syntax
- name: Install Development Tools (dnf)
  ansible.builtin.dnf:
    name: "@development"
    state: present

Cross-Platform Patterns

Using the package Module

# Works on RHEL, Debian, SUSE — auto-detects package manager
- name: Install nginx (any distro)
  ansible.builtin.package:
    name: nginx
    state: present

OS-Specific Package Names

- name: Install Apache
  ansible.builtin.package:
    name: "{{ apache_package }}"
    state: present
  vars:
    apache_package: "{{ 'httpd' if ansible_os_family == 'RedHat' else 'apache2' }}"

Variable-Driven Package List

# group_vars/redhat.yml
packages:
  - httpd
  - mod_ssl
  - php
  - php-mysqlnd

# playbook
- name: Install application stack
  ansible.builtin.yum:
    name: "{{ packages }}"
    state: present

Error Handling

- name: Install with retry on failure
  ansible.builtin.yum:
    name: mypackage
    state: present
  register: yum_result
  retries: 3
  delay: 10
  until: yum_result is not failed

Version Pinning with versionlock

- name: Install yum-plugin-versionlock
  ansible.builtin.yum:
    name: yum-plugin-versionlock
    state: present

- name: Lock package version
  community.general.yum_versionlock:
    name: nginx-1.20.1
    state: present

Practical Example: Web Server Setup

---
- name: Configure web server
  hosts: web_servers
  become: true

  tasks:
    - name: Install web server packages
      ansible.builtin.dnf:
        name:
          - nginx
          - certbot
          - python3-certbot-nginx
          - firewalld
        state: present

    - name: Start and enable services
      ansible.builtin.systemd:
        name: "{{ item }}"
        state: started
        enabled: true
      loop:
        - nginx
        - firewalld

    - name: Open firewall ports
      ansible.posix.firewalld:
        service: "{{ item }}"
        permanent: true
        immediate: true
        state: enabled
      loop:
        - http
        - https

Conclusion

Use ansible.builtin.dnf for RHEL 8+/Fedora and ansible.builtin.yum for RHEL 7. For cross-platform playbooks, use ansible.builtin.package. Always specify state: present (not latest) unless you intentionally want updates, use update_cache: true for fresh repos, and allow_downgrade: true when pinning specific versions. List multiple packages in a single name array — it's faster than looping individual install tasks.