AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible run_once: Execute Tasks Once Across All Hosts (Complete Guide)

By Luca Berton · Published 2024-01-01 · Category: database-automation

How to use Ansible run_once to execute a task on a single host. Combine with delegate_to, serial, and throttle for controlled execution.

Ansible run_once: Execute Tasks Once Across All Hosts (Complete Guide)

The run_once directive tells Ansible to execute a task only once for the entire play, regardless of how many hosts are targeted. The task runs on the first host in the batch, and the result is applied to all hosts.

See also: Ansible run_once vs delegate_to vs serial: Control Task Execution Scope

Basic Usage

- hosts: webservers  # 10 hosts
  tasks:
    - name: Run database migration (once only)
      ansible.builtin.command: /opt/myapp/migrate.sh
      run_once: true
      # Runs on webserver01 only, skipped on webserver02-10

run_once with delegate_to

A common pattern: run a task once on a specific host, regardless of the play's target hosts.

- hosts: webservers
  tasks:
    - name: Create database backup on DB server
      ansible.builtin.command: pg_dump myapp > /backup/pre-deploy.sql
      run_once: true
      delegate_to: db01

- name: Deploy application ansible.builtin.copy: src: myapp.tar.gz dest: /opt/myapp/ # Runs on ALL webservers

- name: Run database migration ansible.builtin.command: /opt/myapp/migrate.sh run_once: true delegate_to: db01

- name: Clear CDN cache ansible.builtin.uri: url: https://api.cdn.example.com/purge method: POST headers: Authorization: "Bearer {{ vault_cdn_token }}" run_once: true delegate_to: localhost

See also: Ansible run_once: Execute Task on Single Host (Complete Guide)

run_once with register

When run_once is combined with register, the variable is available on all hosts:

- hosts: webservers
  tasks:
    - name: Get latest release version
      ansible.builtin.uri:
        url: https://api.github.com/repos/myorg/myapp/releases/latest
        return_content: true
      register: release
      run_once: true
      delegate_to: localhost

- name: Deploy the release on all hosts ansible.builtin.get_url: url: "{{ (release.content | from_json).assets[0].browser_download_url }}" dest: /opt/myapp/release.tar.gz # release variable is available on ALL hosts

run_once with set_fact

- hosts: all
  tasks:
    - name: Generate deployment ID
      ansible.builtin.set_fact:
        deploy_id: "{{ lookup('ansible.builtin.pipe', 'date +%Y%m%d%H%M%S') }}-{{ 999 | random }}"
      run_once: true

- name: Show deploy ID (same on all hosts) ansible.builtin.debug: msg: "Deploy ID: {{ deploy_id }}"

See also: Ansible ignore_errors: Error Handling Best Practices (Complete Guide)

run_once vs serial vs throttle

serial — Rolling Updates

# Process hosts in batches
- hosts: webservers
  serial: 2  # 2 hosts at a time
  tasks:
    - name: Deploy and restart
      ansible.builtin.service:
        name: myapp
        state: restarted

throttle — Limit Concurrent Tasks

- hosts: webservers
  tasks:
    - name: Download artifact (limit concurrent downloads)
      ansible.builtin.get_url:
        url: https://artifacts.example.com/release.tar.gz
        dest: /opt/release.tar.gz
      throttle: 3  # Max 3 simultaneous downloads

Comparison

| Directive | What It Does | Use Case | |-----------|-------------|----------| | run_once: true | Run on first host only | DB migrations, cache purges, one-time setup | | serial: N | Process N hosts per batch | Rolling deployments, zero-downtime updates | | throttle: N | Max N concurrent executions | Rate-limited APIs, bandwidth constraints | | delegate_to: host | Run on specific host | Cross-host operations |

Common Patterns

Zero-Downtime Deployment

- hosts: webservers
  serial: "30%"
  tasks:
    - name: Remove from load balancer
      ansible.builtin.uri:
        url: "https://lb.example.com/api/deregister/{{ inventory_hostname }}"
        method: POST
      delegate_to: localhost

- name: Deploy new version ansible.builtin.unarchive: src: /opt/releases/myapp-latest.tar.gz dest: /opt/myapp/ remote_src: true

- name: Restart application ansible.builtin.systemd: name: myapp state: restarted

- name: Wait for health check ansible.builtin.uri: url: "http://{{ inventory_hostname }}:8080/health" status_code: 200 retries: 10 delay: 3

- name: Re-register in load balancer ansible.builtin.uri: url: "https://lb.example.com/api/register/{{ inventory_hostname }}" method: POST delegate_to: localhost

Pre/Post Deployment Tasks

- hosts: webservers
  tasks:
    - name: "[PRE] Notify deployment start"
      community.general.slack:
        token: "{{ vault_slack_token }}"
        channel: '#deployments'
        msg: "Deploying v{{ app_version }} to {{ ansible_play_hosts | length }} hosts"
      run_once: true
      delegate_to: localhost

- name: Deploy application ansible.builtin.copy: src: "myapp-{{ app_version }}.tar.gz" dest: /opt/myapp/

- name: "[POST] Notify deployment complete" community.general.slack: token: "{{ vault_slack_token }}" channel: '#deployments' msg: "✅ v{{ app_version }} deployed to all hosts" run_once: true delegate_to: localhost

FAQ

What does run_once do in Ansible?

run_once: true executes a task only once for the entire play, on the first host in the batch. The result (including registered variables) is shared with all other hosts. Use it for tasks that should happen once per deployment, like database migrations.

Does run_once set registered variables on all hosts?

Yes. When you combine run_once: true with register, the registered variable is available on all hosts in the play, not just the host where the task ran.

What is the difference between run_once and delegate_to?

run_once controls how many times a task runs (once). delegate_to controls where a task runs (specific host). They're often combined: run_once: true + delegate_to: db01 means "run this once, on db01."

Can I use run_once with serial?

Yes, but be careful. With serial, run_once executes once per batch, not once for the entire play. If you have 10 hosts with serial: 5, the task runs twice (once per batch of 5).

When should I use run_once vs running a separate play?

Use run_once when the task is part of the same workflow (e.g., migrate DB before deploying app). Use a separate play when the task targets different hosts or needs different become/connection settings.

Conclusion

run_once is essential for deployment workflows where certain tasks (migrations, cache purges, notifications) should only happen once. Combine with delegate_to for cross-host operations and serial for rolling updates.

Related Articles

Ansible delegate_to: Run Tasks on Different HostsAnsible Handlers: Run Tasks on ChangeAnsible Playbook: Complete Guide

Category: database-automation

Browse all Ansible tutorials · AnsiblePilot Home