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.
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: presentinstead 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-lintto 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
--checkfirst on production infrastructure. Check mode reveals what would change without actually changing it. - Using
shellorcommandtasks when a dedicated module exists. Modules are idempotent and handle error cases better than raw shell commands. - Forgetting
become: yeswhen 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
handlersfor service restarts. A playbook that restarts Nginx on every task is unnecessary; handlers only trigger once at the end when notified.
Additional Common Mistakes
- 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
- Running everything as root. Use
becomeselectively 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
- Not using
--difffor auditing. See exactly what changes on each run:
ansible-playbook site.yml --diff
Performance Tips
- Use
strategy: freefor 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
- 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
- Use
asyncfor 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 Related Resources
Provision an AWS VPC with Terraform
How to use Terraform to provision a production-ready AWS VPC with public and private subnets, NAT gateways, and security groups
RecipeDocker Basics
How to containerize an application, write a Dockerfile, and run containers with Docker Compose.
RecipeManage Application Secrets Securely
How to store, rotate, and inject API keys, database passwords, and certificates without hardcoding them in source code or environment files.
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.