Separate the layers
A reconciliation batch is approaching its space limit. Storage staff increased capacity presented to the VM, but df is unchanged. Diagnosis must locate the layer not yet expanded: device, PV, VG, LV or filesystem. Free VG capacity is not free filesystem space. Nor does every change require modifying every layer; an existing VG reserve may be sufficient. Record device and UUID, filesystem, mount, usage and free capacity before deciding. A name such as /dev/sdb can change between machines or boots and is not authorization to format a disk.
A repeatable target
The exercise uses an existing lab XFS LV mounted at /srv/dr-batch. Its name is labvg/batch and the target is at least 6 GiB. Before execution, confirm on a disposable RHEL 10 VM that the VG has enough capacity and the volume contains no data to retain. The playbook compares supplied and observed UUIDs and rejects another filesystem type or mount. size: 6g expresses an absolute target; size: +2g expresses an increment that may grow again on every repeat. shrink: false prevents reducing an already larger LV, so the contract is a lower bound rather than exactly 6 GiB.
Growth and recovery
resizefs requests filesystem growth along with the LV for supported types. Still confirm both outcomes: a failure can leave a larger LV without completing filesystem growth. Do not equate that partial state with automatic data loss or declare it resolved from LV size alone. For XFS, do not plan shrinking as a simple reversal of growth; RHEL documentation does not support that reduction. If requirements change to a smaller volume, prepare migration or restoration to an appropriate destination and validate the application. A snapshot alone does not replace a tested recovery plan and may depend on the same limited capacity.
Persistence and acceptance
state: mounted configures both persistent entry and current mount. present handles persistent configuration without guaranteeing the current mount; ephemeral provides a temporary mount without writing fstab. Choose the state matching the contract. To accept the exercise, compare lvs, findmnt and df, verify synthetic data writes and reads, and repeat the playbook. Then reboot the lab VM in a controlled manner and observe the same UUID at the same mountpoint again. An fstab line does not prove device availability at boot or application access through directories.
Exercise status
This example was checked for syntax and module resolution, not executed against actual LVM. Install the collections listed in requirements.yml and supply inventory rhel_lab, lab_confirmed=true and expected_uuid only after preparing the disposable VM. The example starts from an existing LV; it does not provision disks, PVs, VG or the initial filesystem. Those prerequisites and later observations are part of the practical work to perform.
# Exercise for a disposable RHEL 10 VM with an existing XFS lab LV.
# Syntax checked only; no LVM, mount or reboot execution is claimed.
- name: Grow an identified lab filesystem without allowing shrink
hosts: rhel_lab
become: true
gather_facts: true
vars:
lab_vg: labvg
lab_lv: batch
lab_path: /srv/dr-batch
pre_tasks:
- name: Require an explicit disposable lab and expected UUID
ansible.builtin.assert:
that:
- lab_confirmed | default(false) | bool
- ansible_distribution == 'RedHat'
- ansible_distribution_major_version == '10'
- expected_uuid | default('') | length > 0
- name: Read identity of the existing filesystem
ansible.builtin.command:
argv: [blkid, -s, UUID, -o, value, "/dev/{{ lab_vg }}/{{ lab_lv }}"]
register: observed_uuid
changed_when: false
- name: Read existing filesystem type
ansible.builtin.command:
argv: [blkid, -s, TYPE, -o, value, "/dev/{{ lab_vg }}/{{ lab_lv }}"]
register: observed_type
changed_when: false
- name: Reject unexpected volume identity or type before resizing
ansible.builtin.assert:
that:
- observed_uuid.stdout | trim == expected_uuid
- observed_type.stdout | trim == 'xfs'
- name: Confirm that XFS is already mounted at the intended lab path
ansible.builtin.command:
argv: [findmnt, --noheadings, --source, "UUID={{ expected_uuid }}", --output, TARGET]
register: current_mount
changed_when: false
- name: Reject unexpected mount identity before growth
ansible.builtin.assert:
that:
- current_mount.stdout | trim == lab_path
tasks:
- name: Grow to at least six GiB without shrinking a larger LV
community.general.lvol:
vg: "{{ lab_vg }}"
lv: "{{ lab_lv }}"
size: 6g
shrink: false
resizefs: true
state: present
- name: Keep the identified filesystem mounted and configured
ansible.posix.mount:
path: "{{ lab_path }}"
src: "UUID={{ expected_uuid }}"
fstype: xfs
opts: defaults
state: mounted
LV grows from 4 to 6 GiB but df still shows 4: confirm and address the filesystem layer.
Common pitfalls
Repeated increments; treating XFS reduction as rollback; fstab as reboot proof; assuming identity from a disk name.
Related topics: SELinux: mappings and effective state · Services, firewall and external validation
Confirm identity and measure capacity, filesystem, mounting and use separately.
Reference: Logical volume module · EX294 current objectives inspected 2026-09-30; RHCE in Ansible framework effective 2026-05-11; booking product version not publicly pinned