Skip to content

Common Modules

A module is the code that actually does the work in a task. There are thousands; you'll use maybe fifteen day to day. This is that shortlist, with copy-paste examples. Names use the fully qualified form (ansible.builtin.apt) — see Collections for why.

Read the docs for any module

ansible-doc ansible.builtin.copy prints every option and examples right in your terminal. ansible-doc -l lists all installed modules.

Packages

- name: Install one package
  ansible.builtin.apt:
    name: htop
    state: present          # present | latest | absent

- name: Install a list (single transaction — faster than a loop)
  ansible.builtin.apt:
    name: "{{ base_packages }}"
    state: present

- name: Update the cache, but only if it's older than an hour
  ansible.builtin.apt:
    update_cache: true
    cache_valid_time: 3600

- name: Full upgrade
  ansible.builtin.apt:
    upgrade: dist
- name: Install regardless of distro (uses apt/dnf/etc automatically)
  ansible.builtin.package:
    name: git
    state: present
- name: Install a Python package
  ansible.builtin.pip:
    name: borgmatic
    extra_args: --break-system-packages   # needed on modern Debian (PEP 668)

Files & directories

Task Module
Create/remove dirs, set perms, symlinks ansible.builtin.file
Copy a file from the control node ansible.builtin.copy
Render a Jinja2 template ansible.builtin.template
Edit one line in an existing file ansible.builtin.lineinfile
Download a file from a URL ansible.builtin.get_url
Extract a tarball/zip ansible.builtin.unarchive
- name: Ensure a directory exists with the right owner
  ansible.builtin.file:
    path: /opt/docker/app
    state: directory          # directory | file | touch | link | absent
    owner: "{{ main_user }}"
    group: "{{ main_user }}"
    mode: "0755"

- name: Copy a static file
  ansible.builtin.copy:
    src: motd.txt             # from the role's files/ dir
    dest: /etc/motd
    mode: "0644"

- name: Render a template (variables get filled in)
  ansible.builtin.template:
    src: config.yml.j2        # from the role's templates/ dir
    dest: /etc/app/config.yml
    validate: "app --test-config %s"   # optional: validate before saving

- name: Ensure a single line is present in a file
  ansible.builtin.lineinfile:
    path: /etc/sysctl.conf
    line: "net.ipv4.ip_forward = 1"
    state: present

- name: Download a file only if it isn't already there
  ansible.builtin.get_url:
    url: https://example.com/tool.tar.gz
    dest: /tmp/tool.tar.gz

- name: Extract an archive to a remote path
  ansible.builtin.unarchive:
    src: /tmp/tool.tar.gz
    dest: /opt/tool
    remote_src: true          # the archive is already on the managed node

copy vs template

copy places a file verbatim. template runs it through Jinja2 first, filling in {{ variables }}. If the file has variables, use template and name it *.j2.

Services

- name: Start now and enable at boot
  ansible.builtin.service:
    name: docker
    state: started            # started | stopped | restarted | reloaded
    enabled: true

- name: systemd-specific (daemon-reload after a unit change)
  ansible.builtin.systemd_service:
    name: myapp
    state: restarted
    daemon_reload: true

Users, groups & cron

- name: Create a user
  ansible.builtin.user:
    name: appuser
    groups: docker
    append: true              # ADD to groups (without this, it REPLACES them)
    shell: /bin/bash

- name: Create a group
  ansible.builtin.group:
    name: appusers

- name: Add a cron job
  ansible.builtin.cron:
    name: "nightly backup"    # unique identifier — edits in place on re-run
    minute: "0"
    hour: "3"
    job: "/usr/local/bin/backup.sh"

Commands (last resort)

Use these only when no module fits — they aren't idempotent on their own.

- name: Run a command (no shell — no pipes/redirects/globs)
  ansible.builtin.command:
    cmd: docker compose up -d
    chdir: /opt/docker/app    # cd here first
    creates: /opt/app/.installed   # SKIP if this path already exists (idempotency)

- name: Run through a shell (needed for | > < && $VAR)
  ansible.builtin.shell: |
    curl -fsSL https://example.com/key | gpg --dearmor -o /etc/apt/keyrings/x.gpg
  args:
    creates: /etc/apt/keyrings/x.gpg

command vs shell

command does not run in a shell, so |, >, &&, *, and $VAR won't work — it's safer. Use shell only when you need those. Add creates: (or a when:) so they don't report changed every run. See Writing Tasks → changed_when.

Filesystem mounts (ansible.posix)

- name: Mount the NAS backup share and add it to fstab
  ansible.posix.mount:
    path: /mnt/backups
    src: "//nas.internal/backups"
    fstype: cifs
    opts: "credentials=/etc/samba/nas-creds,uid=1000,gid=1000"
    state: mounted            # mounted | present | unmounted | absent

Debugging & facts

- name: Print a message or variable
  ansible.builtin.debug:
    msg: "Deploying to {{ inventory_hostname }}"

- name: Print a variable's value
  ansible.builtin.debug:
    var: base_packages

- name: Gather system facts on demand
  ansible.builtin.setup:
    filter: ansible_distribution*

See Facts & Variables for what setup collects.

Where these come from

  • ansible.builtin.* — ship with Ansible, always available.
  • ansible.posix.* (e.g. mount) and community.general.* (e.g. timezone) — live in collections you install. See Collections & Galaxy.

Ref: Ansible — Module Index