Introduction

ansible.builtin.wait_for pauses playbook execution until a condition is met — a port opens, a file appears, a string shows up in a log, or a process finishes. Essential for deployment orchestration where services take time to start.

Wait for Port

# Wait for service to start listening
- name: Wait for nginx
  ansible.builtin.wait_for:
    port: 80
    timeout: 30

# Wait for remote port
- name: Wait for database
  ansible.builtin.wait_for:
    host: db.internal
    port: 5432
    timeout: 60

# Wait for app to start
- name: Wait for application
  ansible.builtin.wait_for:
    port: 8080
    delay: 5        # Wait 5s before first check
    timeout: 60     # Give up after 60s

Parameters

ParameterDefaultDescription
port—TCP port to check
host127.0.0.1Host to check
timeout300Max seconds to wait
delay0Seconds to wait before first check
sleep1Seconds between checks
statestartedstarted, stopped, present, absent, drained
path—File path to check
search_regex—Regex to search in file or port output
msg—Custom timeout error message
connect_timeout5Connection timeout per check
exclude_hosts—Hosts to exclude (for drained)
active_connection_states—Connection states for drained

Wait for Port to Close

# Wait for old service to stop
- name: Wait for port 8080 to be free
  ansible.builtin.wait_for:
    port: 8080
    state: stopped
    timeout: 30

# Wait for connections to drain before stopping
- name: Wait for connections to drain
  ansible.builtin.wait_for:
    port: 80
    state: drained
    timeout: 60
    exclude_hosts: 10.0.0.1    # Exclude monitoring

Wait for File

# Wait for file to appear
- name: Wait for lock file to be created
  ansible.builtin.wait_for:
    path: /tmp/app.lock
    state: present
    timeout: 30

# Wait for file to be removed
- name: Wait for lock file to be released
  ansible.builtin.wait_for:
    path: /tmp/app.lock
    state: absent
    timeout: 120

# Wait for content in file
- name: Wait for app to be ready
  ansible.builtin.wait_for:
    path: /var/log/myapp/startup.log
    search_regex: "Application started"
    timeout: 60

Wait for SSH

# Wait for host to be reachable after reboot
- name: Reboot server
  ansible.builtin.reboot:
    reboot_timeout: 300

# Or manually
- name: Reboot
  ansible.builtin.command: shutdown -r now
  async: 1
  poll: 0

- name: Wait for SSH
  ansible.builtin.wait_for:
    host: "{{ ansible_host }}"
    port: 22
    delay: 30
    timeout: 300
  delegate_to: localhost

# Or use wait_for_connection
- name: Wait for connection
  ansible.builtin.wait_for_connection:
    delay: 30
    timeout: 300

Deployment Patterns

Rolling Deployment

- name: Stop application
  ansible.builtin.service:
    name: myapp
    state: stopped

- name: Wait for port to close
  ansible.builtin.wait_for:
    port: 8080
    state: stopped
    timeout: 30

- name: Deploy new version
  ansible.builtin.copy:
    src: "myapp-{{ version }}.jar"
    dest: /opt/myapp/myapp.jar

- name: Start application
  ansible.builtin.service:
    name: myapp
    state: started

- name: Wait for app to be ready
  ansible.builtin.wait_for:
    port: 8080
    delay: 10
    timeout: 60

- name: Verify health
  ansible.builtin.uri:
    url: http://localhost:8080/health
    status_code: 200
  retries: 5
  delay: 5

Database Migration

- name: Wait for database to be available
  ansible.builtin.wait_for:
    host: "{{ db_host }}"
    port: 5432
    timeout: 120

- name: Run migrations
  ansible.builtin.command: /opt/myapp/migrate.sh
  register: migration

- name: Wait for migration log completion
  ansible.builtin.wait_for:
    path: /var/log/migration.log
    search_regex: "Migration complete"
    timeout: 300

Multi-Service Startup

- name: Start all services
  ansible.builtin.service:
    name: "{{ item }}"
    state: started
  loop:
    - redis
    - postgresql
    - myapp

- name: Wait for all services
  ansible.builtin.wait_for:
    port: "{{ item.port }}"
    delay: 2
    timeout: 30
  loop:
    - { name: redis, port: 6379 }
    - { name: postgresql, port: 5432 }
    - { name: myapp, port: 8080 }

Troubleshooting

Timeout Too Short

Default is 300s (5 min). Java apps may need more:

- wait_for:
    port: 8080
    delay: 15      # JVM needs startup time
    timeout: 120   # 2 minutes

Checking Remote Port from localhost

Use delegate_to:

- name: Check remote port from controller
  ansible.builtin.wait_for:
    host: "{{ inventory_hostname }}"
    port: 443
    timeout: 30
  delegate_to: localhost

Conclusion

wait_for bridges the gap between "I started the service" and "the service is actually ready." Always wait for ports after starting services. Use delay for apps with slow startup (Java, databases). Use search_regex to wait for specific log messages. Use state: drained before stopping services in load-balanced environments. It's the glue that makes deployment orchestration reliable.