Volumes are one of those things you don’t think about—until something breaks. A missing mount, a misconfigured filesystem, or a non-persistent volume can take down an otherwise perfectly automated system.
Ansible doesn’t have a single “volume” abstraction like container platforms do. Instead, it gives you building blocks: disk formatting, filesystem creation, mounting, and persistence. Once you understand how these pieces fit together, you can fully automate storage across your infrastructure.
What “Volumes” Mean in Ansible
In Ansible, managing volumes usually involves three steps:
- Preparing the device (partitioning or identifying the disk)
- Creating a filesystem
- Mounting the volume and ensuring persistence
These steps map to specific modules:
- ansible.builtin.filesystem → create filesystems
- ansible.builtin.mount → mount and persist volumes
- ansible.builtin.parted → optional disk partitioning
A Simple Volume Setup Example
Let’s start with a common scenario: attaching a new disk (/dev/xvdf) and mounting it at /data.
Step 1: Create a Filesystem
Before mounting, the disk needs a filesystem:
1- name: Create ext4 filesystem on disk
2 ansible.builtin.filesystem:
3 fstype: ext4
4 dev: /dev/xvdf
5This is idempotent—Ansible won’t recreate the filesystem if it already exists.
Step 2: Mount the Volume
1- name: Mount the volume
2 ansible.builtin.mount:
3 path: /data
4 src: /dev/xvdf
5 fstype: ext4
6 state: mounted
7This does two things:
- Mounts the volume immediately
- Adds it to
/etc/fstabfor persistence
Here’s Where Things Get Interesting
The mount module is more powerful than it looks. Its state parameter controls behavior:
- mounted → mount and persist
- unmounted → unmount but keep fstab entry
- absent → remove from fstab
- present → add to fstab but don’t mount
This gives you fine-grained control over system state—especially useful in rolling deployments or migrations.
Making Mounts Persistent (and Safe)
A common mistake developers make is assuming a mounted volume will survive a reboot. Without an /etc/fstab entry, it won’t.
Using Ansible ensures persistence automatically when state: mounted is used. But you can also explicitly define mount options:
1- name: Mount volume with options
2 ansible.builtin.mount:
3 path: /data
4 src: /dev/xvdf
5 fstype: ext4
6 opts: noatime,nodiratime
7 state: mounted
8These options can improve performance by reducing disk writes.
Working with Multiple Volumes
In real systems, you rarely manage just one disk. You might have logs, backups, and application data on separate volumes.
Instead of repeating tasks, define volumes as data:
1volumes:
2 - device: /dev/xvdf
3 mount_point: /data
4 - device: /dev/xvdg
5 mount_point: /logs
6Then loop through them:
1- name: Create filesystems
2 ansible.builtin.filesystem:
3 fstype: ext4
4 dev: "{{ item.device }}"
5 loop: "{{ volumes }}"
6
7- name: Mount volumes
8 ansible.builtin.mount:
9 path: "{{ item.mount_point }}"
10 src: "{{ item.device }}"
11 fstype: ext4
12 state: mounted
13 loop: "{{ volumes }}"
14This pattern keeps your playbooks clean and scalable.
Handling Partitioned Disks
If your disk isn’t pre-partitioned, you can automate that too:
1- name: Create partition
2 ansible.builtin.parted:
3 device: /dev/xvdf
4 number: 1
5 state: present
6 part_type: primary
7 fs_type: ext4
8Then reference the partition:
1dev: /dev/xvdf1This is especially useful in cloud environments where raw disks are attached.
Volume Management in Containers (Bonus Context)
If you’re using Ansible with Docker, volumes show up differently. Instead of mounting block devices, you define Docker volumes:
1- name: Create Docker volume
2 community.docker.docker_volume:
3 name: app_data
4And attach it to containers:
1- name: Run container with volume
2 community.docker.docker_container:
3 name: my_app
4 image: my_image
5 volumes:
6 - app_data:/var/lib/app
7Different abstraction—but same goal: persistent storage.
Gotchas You’ll Eventually Hit
- Device naming changes: Cloud providers may rename devices after reboot. Consider using UUIDs.
- Filesystem mismatch: Mounting with the wrong
fstypewill fail silently in some cases. - Permissions: Mounting works, but apps fail due to ownership issues.
Using UUID instead of device paths is more reliable:
1- name: Mount using UUID
2 ansible.builtin.mount:
3 path: /data
4 src: UUID=1234-5678
5 fstype: ext4
6 state: mounted
7Performance and Reliability Tips
Volume management isn’t just about making things work—it’s about making them stable and efficient.
- Use noatime to reduce disk writes
- Separate high-IO workloads onto dedicated volumes
- Validate mounts after provisioning in CI/CD pipelines
- Avoid hardcoding device names in dynamic environments
Putting It All Together
A minimal but production-ready playbook might look like this:
1- hosts: all
2 become: yes
3 vars:
4 volumes:
5 - device: /dev/xvdf
6 mount_point: /data
7
8 tasks:
9 - name: Ensure filesystem exists
10 ansible.builtin.filesystem:
11 fstype: ext4
12 dev: "{{ item.device }}"
13 loop: "{{ volumes }}"
14
15 - name: Ensure mount point exists
16 ansible.builtin.file:
17 path: "{{ item.mount_point }}"
18 state: directory
19 loop: "{{ volumes }}"
20
21 - name: Mount volumes
22 ansible.builtin.mount:
23 path: "{{ item.mount_point }}"
24 src: "{{ item.device }}"
25 fstype: ext4
26 state: mounted
27 loop: "{{ volumes }}"
28Clean, repeatable, and fully automated.
Final Thought
Ansible doesn’t try to hide how storage works—it exposes it. That might feel verbose at first, but it gives you precise control over how volumes are created, mounted, and maintained.
Once you internalize these patterns, managing volumes becomes just another predictable part of your infrastructure code.