├── .ansible-lint ├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ ├── release.yml │ └── stale.yml ├── .gitignore ├── .yamllint ├── LICENSE ├── README.md ├── defaults └── main.yml ├── meta └── main.yml ├── molecule └── default │ ├── converge.yml │ └── molecule.yml └── tasks ├── check-size.yml ├── disable.yml ├── enable.yml └── main.yml /.ansible-lint: -------------------------------------------------------------------------------- 1 | skip_list: 2 | - 'yaml' 3 | - 'role-name' 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | --- 3 | github: geerlingguy 4 | patreon: geerlingguy 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: CI 3 | 'on': 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | schedule: 9 | - cron: "0 2 * * 2" 10 | 11 | defaults: 12 | run: 13 | working-directory: 'geerlingguy.swap' 14 | 15 | jobs: 16 | 17 | lint: 18 | name: Lint 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Check out the codebase. 22 | uses: actions/checkout@v4 23 | with: 24 | path: 'geerlingguy.swap' 25 | 26 | - name: Set up Python 3. 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: '3.x' 30 | 31 | - name: Install test dependencies. 32 | run: pip3 install yamllint 33 | 34 | - name: Lint code. 35 | run: | 36 | yamllint . 37 | 38 | molecule: 39 | name: Molecule 40 | runs-on: ubuntu-latest 41 | strategy: 42 | matrix: 43 | distro: 44 | - rockylinux9 45 | - ubuntu2004 46 | - debian11 47 | 48 | steps: 49 | - name: Check out the codebase. 50 | uses: actions/checkout@v4 51 | with: 52 | path: 'geerlingguy.swap' 53 | 54 | - name: Set up Python 3. 55 | uses: actions/setup-python@v5 56 | with: 57 | python-version: '3.x' 58 | 59 | - name: Install test dependencies. 60 | run: pip3 install ansible molecule molecule-plugins[docker] docker 61 | 62 | - name: Run Molecule tests. 63 | run: molecule test 64 | env: 65 | PY_COLORS: '1' 66 | ANSIBLE_FORCE_COLOR: '1' 67 | MOLECULE_DISTRO: ${{ matrix.distro }} 68 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This workflow requires a GALAXY_API_KEY secret present in the GitHub 3 | # repository or organization. 4 | # 5 | # See: https://github.com/marketplace/actions/publish-ansible-role-to-galaxy 6 | # See: https://github.com/ansible/galaxy/issues/46 7 | 8 | name: Release 9 | 'on': 10 | push: 11 | tags: 12 | - '*' 13 | 14 | defaults: 15 | run: 16 | working-directory: 'geerlingguy.swap' 17 | 18 | jobs: 19 | 20 | release: 21 | name: Release 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Check out the codebase. 25 | uses: actions/checkout@v4 26 | with: 27 | path: 'geerlingguy.swap' 28 | 29 | - name: Set up Python 3. 30 | uses: actions/setup-python@v5 31 | with: 32 | python-version: '3.x' 33 | 34 | - name: Install Ansible. 35 | run: pip3 install ansible-core 36 | 37 | - name: Trigger a new import on Galaxy. 38 | run: >- 39 | ansible-galaxy role import --api-key ${{ secrets.GALAXY_API_KEY }} 40 | $(echo ${{ github.repository }} | cut -d/ -f1) $(echo ${{ github.repository }} | cut -d/ -f2) 41 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Close inactive issues 3 | 'on': 4 | schedule: 5 | - cron: "55 3 * * 5" # semi-random time 6 | 7 | jobs: 8 | close-issues: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | pull-requests: write 13 | steps: 14 | - uses: actions/stale@v8 15 | with: 16 | days-before-stale: 120 17 | days-before-close: 60 18 | exempt-issue-labels: bug,pinned,security,planned 19 | exempt-pr-labels: bug,pinned,security,planned 20 | stale-issue-label: "stale" 21 | stale-pr-label: "stale" 22 | stale-issue-message: | 23 | This issue has been marked 'stale' due to lack of recent activity. If there is no further activity, the issue will be closed in another 30 days. Thank you for your contribution! 24 | 25 | Please read [this blog post](https://www.jeffgeerling.com/blog/2020/enabling-stale-issue-bot-on-my-github-repositories) to see the reasons why I mark issues as stale. 26 | close-issue-message: | 27 | This issue has been closed due to inactivity. If you feel this is in error, please reopen the issue or file a new issue with the relevant details. 28 | stale-pr-message: | 29 | This pr has been marked 'stale' due to lack of recent activity. If there is no further activity, the issue will be closed in another 30 days. Thank you for your contribution! 30 | 31 | Please read [this blog post](https://www.jeffgeerling.com/blog/2020/enabling-stale-issue-bot-on-my-github-repositories) to see the reasons why I mark issues as stale. 32 | close-pr-message: | 33 | This pr has been closed due to inactivity. If you feel this is in error, please reopen the issue or file a new issue with the relevant details. 34 | repo-token: ${{ secrets.GITHUB_TOKEN }} 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.retry 2 | */__pycache__ 3 | *.pyc 4 | .cache 5 | 6 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | rules: 5 | line-length: 6 | max: 120 7 | level: warning 8 | 9 | ignore: | 10 | .github/workflows/stale.yml 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Jeff Geerling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ansible Role: Swap 2 | 3 | [![CI](https://github.com/geerlingguy/ansible-role-swap/actions/workflows/ci.yml/badge.svg)](https://github.com/geerlingguy/ansible-role-swap/actions/workflows/ci.yml) 4 | 5 | An Ansible Role that configures swap space on Linux. 6 | 7 | ## Requirements 8 | 9 | None. 10 | 11 | ## Role Variables 12 | 13 | Available variables are listed below, along with default values (see `defaults/main.yml`): 14 | 15 | swap_file_path: /swapfile 16 | 17 | The location of the swap file on the server. 18 | 19 | swap_file_size_mb: '512' 20 | 21 | How large (in mebibytes) to make the swap file. 22 | 23 | swap_swappiness: '60' 24 | 25 | The `vm.swappiness` value to be configured in sysconfig. 26 | 27 | swap_file_state: present 28 | 29 | If you wish to _remove_ your swapfile, and disable swap, set this to `absent`. Generally you'd probably want to set this to `present`. 30 | 31 | swap_file_create_command: "dd if=/dev/zero of={{ swap_file_path }} bs=1M count={{ swap_file_size_mb }}" 32 | 33 | The command used to create the swap file. You could switch to using `fallocate` to write the swap file more quickly, though there may be inconsistencies if not writing the file with `dd`. 34 | 35 | ## Dependencies 36 | 37 | None. 38 | 39 | ## Example Playbook 40 | 41 | - hosts: all 42 | 43 | vars: 44 | swap_file_size_mb: '1024' 45 | 46 | roles: 47 | - geerlingguy.swap 48 | 49 | ## License 50 | 51 | MIT / BSD 52 | 53 | ## Author Information 54 | 55 | This role was created in 2018 by [Jeff Geerling](https://www.jeffgeerling.com/), author of [Ansible for DevOps](https://www.ansiblefordevops.com/). 56 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | swap_file_path: /swapfile 3 | swap_file_size_mb: '512' 4 | swap_swappiness: '60' 5 | swap_file_state: present 6 | swap_file_create_command: "dd if=/dev/zero of={{ swap_file_path }} bs=1M count={{ swap_file_size_mb }}" 7 | 8 | swap_test_mode: false 9 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: [] 3 | 4 | galaxy_info: 5 | role_name: swap 6 | author: geerlingguy 7 | description: Swap file and swap management for Linux. 8 | issue_tracker_url: https://github.com/geerlingguy/ansible-role-swap/issues 9 | company: "Midwestern Mac, LLC" 10 | license: "license (BSD, MIT)" 11 | min_ansible_version: 2.10 12 | platforms: 13 | - name: Debian 14 | versions: 15 | - all 16 | - name: Ubuntu 17 | versions: 18 | - all 19 | galaxy_tags: 20 | - system 21 | - server 22 | - swap 23 | - swapfile 24 | - pagefile 25 | - performance 26 | - memory 27 | - ram 28 | -------------------------------------------------------------------------------- /molecule/default/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | #become: true 5 | 6 | vars: 7 | swap_file_size_mb: '256' 8 | swap_test_mode: true 9 | 10 | pre_tasks: 11 | - name: Update apt cache. 12 | apt: update_cache=true cache_valid_time=600 13 | when: ansible_os_family == 'Debian' 14 | 15 | roles: 16 | - role: geerlingguy.swap 17 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | role_name_check: 1 3 | dependency: 4 | name: galaxy 5 | options: 6 | ignore-errors: true 7 | driver: 8 | name: docker 9 | platforms: 10 | - name: instance 11 | image: "geerlingguy/docker-${MOLECULE_DISTRO:-rockylinux9}-ansible:latest" 12 | command: ${MOLECULE_DOCKER_COMMAND:-""} 13 | volumes: 14 | - /sys/fs/cgroup:/sys/fs/cgroup:rw 15 | cgroupns_mode: host 16 | privileged: true 17 | pre_build_image: true 18 | provisioner: 19 | name: ansible 20 | playbooks: 21 | converge: ${MOLECULE_PLAYBOOK:-converge.yml} 22 | -------------------------------------------------------------------------------- /tasks/check-size.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check if swap file exists 3 | stat: 4 | path: "{{ swap_file_path }}" 5 | get_checksum: false 6 | register: swap_file_check 7 | changed_when: false 8 | 9 | - name: Set variable for existing swap file size 10 | set_fact: 11 | swap_file_existing_size_mb: "{{ (swap_file_check.stat.size / 1024 / 1024) | int }}" 12 | when: swap_file_check.stat.exists 13 | 14 | - name: Set default value for existing swap file size when it doesn't exist 15 | set_fact: 16 | swap_file_existing_size_mb: "{{ swap_file_size_mb }}" 17 | when: not swap_file_check.stat.exists 18 | -------------------------------------------------------------------------------- /tasks/disable.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Disable swap (if configured). 3 | command: swapoff -a 4 | when: ansible_memory_mb['swap']['total'] > 0 5 | tags: ['skip_ansible_lint'] 6 | 7 | - name: Ensure swap file doesn't exist (if configured). 8 | file: 9 | path: "{{ swap_file_path }}" 10 | state: absent 11 | -------------------------------------------------------------------------------- /tasks/enable.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure swap file exists. 3 | command: > 4 | {{ swap_file_create_command }} 5 | creates='{{ swap_file_path }}' 6 | register: swap_file_create 7 | 8 | - name: Set permissions on swap file. 9 | file: 10 | path: "{{ swap_file_path }}" 11 | owner: root 12 | group: root 13 | mode: 0600 14 | 15 | - name: Make swap file if necessary. 16 | command: mkswap {{ swap_file_path }} 17 | when: swap_file_create is changed 18 | register: mkswap_result 19 | 20 | - name: Run swapon on the swap file. 21 | command: swapon {{ swap_file_path }} 22 | when: 23 | - mkswap_result is changed 24 | - not swap_test_mode 25 | 26 | - name: Set swappiness. 27 | sysctl: 28 | name: vm.swappiness 29 | value: "{{ swap_swappiness }}" 30 | state: present 31 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Manage swap file entry in fstab. 3 | mount: 4 | name: none 5 | src: "{{ swap_file_path }}" 6 | fstype: swap 7 | opts: sw 8 | state: "{{ swap_file_state }}" 9 | 10 | - include_tasks: check-size.yml 11 | when: swap_file_state == 'present' 12 | 13 | - include_tasks: disable.yml 14 | when: swap_file_state == 'absent' 15 | or (swap_file_state == 'present' and swap_file_existing_size_mb != swap_file_size_mb) 16 | 17 | - include_tasks: enable.yml 18 | when: swap_file_state == 'present' 19 | --------------------------------------------------------------------------------