intermediate By Mathias Paulenko

Ansible Playbook for Server Configuration

How to write and run Ansible playbooks for provisioning, configuring, and managing servers with idempotent tasks, roles, and inventory files.

Topics: devops

Note: This guide follows English-language naming conventions and terminology standards common in international development teams. Examples use English identifiers and comments to maximize compatibility across codebases and tooling.

Overview

Ansible is an agentless automation tool that uses YAML playbooks to configure servers, deploy applications, and orchestrate infrastructure. Unlike tools that require a daemon on every target host, Ansible connects over SSH and executes tasks remotely, making it lightweight and easy to adopt.

Before configuration management tools, sysadmins logged into each server manually to install packages, edit config files, and restart services. This was slow, error-prone, and impossible to audit. Ansible replaces this with declarative, idempotent playbooks that can configure hundreds of servers in minutes and be version-controlled like application code.

When to Use

Use this recipe when:

  • Provisioning new servers with a consistent baseline (packages, users, SSH keys).
  • Deploying application updates across multiple web servers in parallel.
  • Ensuring all production nodes share the same configuration (Nginx, PostgreSQL, Redis).
  • Running ad-hoc commands across an entire fleet (e.g., restart all services after a security patch).
  • Managing secrets with Ansible Vault instead of hardcoding passwords in playbooks.

What works

  • Make tasks idempotent. Running a playbook twice should not change anything on the second run. Use state: present instead of shell commands that blindly install packages.
  • Use roles for reusability. Extract common tasks into roles that can be shared across projects and teams via Ansible Galaxy or a private Git repository.
  • Version control everything. Playbooks, inventories, and variables should live in git so changes are peer-reviewed and reversible.
  • Use ansible-lint to enforce style and catch common errors before running playbooks in production.
  • Encrypt secrets with Ansible Vault instead of storing passwords in plaintext. Never commit unencrypted credentials.

Common Mistakes

  • Running playbooks without --check first on production infrastructure. Check mode reveals what would change without actually changing it.
  • Using shell or command tasks when a dedicated module exists. Modules are idempotent and handle error cases better than raw shell commands.
  • Forgetting become: yes when tasks require root privileges, causing cryptic permission errors.
  • Hardcoding IP addresses in inventory files. Use DNS names or dynamic inventory scripts (AWS, GCP) that stay current as infrastructure scales.
  • Not using handlers for service restarts. A playbook that restarts Nginx on every task is unnecessary; handlers only trigger once at the end when notified.

Additional Common Mistakes

  1. Not caching facts. Fact gathering is slow across many hosts:
# ansible.cfg
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = ./.facts
fact_caching_timeout = 86400
  1. Running everything as root. Use become selectively per task:
# Bad: everything runs as root
- hosts: all
  become: yes
  tasks:
    - name: Clone repo as root
      ansible.builtin.git:
        repo: https://github.com/myorg/myapp.git
        dest: /home/deployer/myapp

# Good: become only where needed
- hosts: all
  tasks:
    - name: Install packages
      ansible.builtin.apt:
        name: nginx
        state: present
      become: yes

    - name: Clone repo as deployer
      ansible.builtin.git:
        repo: https://github.com/myorg/myapp.git
        dest: /home/deployer/myapp
      become: yes
      become_user: deployer
  1. Not using --diff for auditing. See exactly what changes on each run:
ansible-playbook site.yml --diff

Performance Tips

  1. Use strategy: free for faster execution. Each host runs independently:
- name: Fast parallel deployment
  hosts: webservers
  strategy: free
  tasks:
    - name: Deploy app
      ansible.builtin.git:
        repo: https://github.com/myorg/myapp.git
        dest: /var/www/myapp
  1. Disable fact gathering when unused. Saves 2-5 seconds per host:
- name: Simple task without facts
  hosts: webservers
  gather_facts: no
  tasks:
    - name: Restart service
      ansible.builtin.service:
        name: nginx
        state: restarted
  1. Use async for long-running tasks. Don’t block the playbook:
- name: Run slow backup
  ansible.builtin.shell: pg_dump mydb > /tmp/backup.sql
  async: 300
  poll: 5

Frequently Asked Questions

What is the advantage of Ansible over shell scripts?
Ansible playbooks are idempotent and declarative. Running the same playbook twice produces the same state without duplicating configuration or causing errors.
How do I manage secrets in Ansible?
Use Ansible Vault to encrypt sensitive files, or integrate with external secret managers like HashiCorp Vault or AWS Secrets Manager. Never commit plain-text passwords.
What is an Ansible inventory?
An inventory lists the managed hosts and groups them logically (e.g., web, db, cache). Inventories can be static files or dynamic plugins pulled from cloud providers.