Introduction

Swap space extends available memory by using disk as overflow. Ansible automates swap management — create swap files or partitions, set permissions, activate swap, persist via fstab, and tune swappiness. This is essential for servers that need memory safety nets without manual configuration.

Create a Swap File

---
- name: Create and enable swap file
  hosts: all
  become: true
  vars:
    swap_file_path: /swapfile
    swap_file_size_mb: 2048
    swappiness: 10
  tasks:
    - name: Check if swap file exists
      ansible.builtin.stat:
        path: "{{ swap_file_path }}"
      register: swap_file

    - name: Create swap file
      ansible.builtin.command: >
        dd if=/dev/zero of={{ swap_file_path }}
        bs=1M count={{ swap_file_size_mb }}
      args:
        creates: "{{ swap_file_path }}"
      when: not swap_file.stat.exists

    - name: Set swap file permissions
      ansible.builtin.file:
        path: "{{ swap_file_path }}"
        mode: '0600'
        owner: root
        group: root

    - name: Format as swap
      ansible.builtin.command: mkswap {{ swap_file_path }}
      when: not swap_file.stat.exists
      register: mkswap_result
      changed_when: mkswap_result.rc == 0

    - name: Enable swap
      ansible.builtin.command: swapon {{ swap_file_path }}
      when: not swap_file.stat.exists
      register: swapon_result
      changed_when: swapon_result.rc == 0

    - name: Add to fstab
      ansible.posix.mount:
        src: "{{ swap_file_path }}"
        path: none
        fstype: swap
        opts: sw
        state: present

    - name: Set swappiness
      ansible.posix.sysctl:
        name: vm.swappiness
        value: "{{ swappiness }}"
        sysctl_set: true
        reload: true

    - name: Set vfs_cache_pressure
      ansible.posix.sysctl:
        name: vm.vfs_cache_pressure
        value: "50"
        sysctl_set: true
        reload: true

Using fallocate (Faster)

- name: Create swap file with fallocate
  ansible.builtin.command: >
    fallocate -l {{ swap_file_size_mb }}M {{ swap_file_path }}
  args:
    creates: "{{ swap_file_path }}"

Note: fallocate doesn't work on all filesystems (e.g., XFS with copy-on-write). Use dd as the safe default.

Swap Partition

- name: Create swap partition
  community.general.parted:
    device: /dev/sdb
    number: 1
    flags: [swap]
    state: present
    part_type: primary
    fs_type: linux-swap

- name: Format swap partition
  ansible.builtin.command: mkswap /dev/sdb1
  register: mkswap
  changed_when: mkswap.rc == 0

- name: Enable swap partition
  ansible.builtin.command: swapon /dev/sdb1
  register: swapon
  changed_when: swapon.rc == 0

- name: Persist in fstab
  ansible.posix.mount:
    src: /dev/sdb1
    path: none
    fstype: swap
    opts: sw
    state: present

Resize Swap

- name: Resize swap file
  hosts: all
  become: true
  vars:
    swap_file_path: /swapfile
    new_size_mb: 4096
  tasks:
    - name: Disable current swap
      ansible.builtin.command: swapoff {{ swap_file_path }}
      changed_when: true
      ignore_errors: true

    - name: Resize swap file
      ansible.builtin.command: >
        dd if=/dev/zero of={{ swap_file_path }}
        bs=1M count={{ new_size_mb }}
      changed_when: true

    - name: Set permissions
      ansible.builtin.file:
        path: "{{ swap_file_path }}"
        mode: '0600'

    - name: Reformat
      ansible.builtin.command: mkswap {{ swap_file_path }}
      changed_when: true

    - name: Re-enable
      ansible.builtin.command: swapon {{ swap_file_path }}
      changed_when: true

Remove Swap

- name: Remove swap file
  hosts: all
  become: true
  tasks:
    - name: Disable swap
      ansible.builtin.command: swapoff /swapfile
      changed_when: true
      ignore_errors: true

    - name: Remove from fstab
      ansible.posix.mount:
        path: none
        src: /swapfile
        fstype: swap
        state: absent

    - name: Delete swap file
      ansible.builtin.file:
        path: /swapfile
        state: absent

Monitor Swap Usage

- name: Check swap usage
  ansible.builtin.shell: |
    free -m | awk '/Swap/ {printf "total=%sMB used=%sMB free=%sMB usage=%s%%\n", $2, $3, $4, ($3/$2)*100}'
  register: swap_info
  changed_when: false

- name: Alert if swap usage > 80%
  ansible.builtin.fail:
    msg: "High swap usage on {{ inventory_hostname }}: {{ swap_info.stdout }}"
  when: swap_info.stdout | regex_search('usage=(\d+)', '\\1') | first | int > 80
  ignore_errors: true

Conditional Swap Creation

- name: Create swap only if RAM < 4GB
  block:
    - name: Create 2GB swap file
      ansible.builtin.command: dd if=/dev/zero of=/swapfile bs=1M count=2048
      args:
        creates: /swapfile

    - name: Setup swap
      ansible.builtin.shell: |
        chmod 600 /swapfile
        mkswap /swapfile
        swapon /swapfile
  when: ansible_memtotal_mb < 4096
RAMSwap (No Hibernation)Swap (With Hibernation)
≤ 2 GB2× RAM3× RAM
2–8 GBEqual to RAM2× RAM
8–64 GB≥ 4 GB1.5× RAM
> 64 GB≥ 4 GBNot recommended
# Auto-calculate swap size
- name: Calculate swap size
  ansible.builtin.set_fact:
    swap_size_mb: >-
      {{ [ansible_memtotal_mb, 4096] | min
         if ansible_memtotal_mb > 8192
         else ansible_memtotal_mb }}

Troubleshooting

"swapon: /swapfile: insecure permissions"

Swap file must be 0600:

- ansible.builtin.file:
    path: /swapfile
    mode: '0600'
    owner: root

"swapon failed: Invalid argument"

The file wasn't formatted with mkswap:

- ansible.builtin.command: mkswap /swapfile

Swap Not Persisting After Reboot

Ensure fstab entry exists:

- ansible.posix.mount:
    src: /swapfile
    path: none
    fstype: swap
    opts: sw
    state: present

Conclusion

Ansible manages swap with dd or fallocate to create the file, mkswap to format, swapon to enable, and ansible.posix.mount to persist in fstab. Tune vm.swappiness (10 for servers, 60 for desktops) and vm.vfs_cache_pressure via ansible.posix.sysctl. Auto-calculate swap sizes from ansible_memtotal_mb for dynamic provisioning across heterogeneous fleets.