Configuration Management — Ansible
Configuring servers idempotently. When and why you still need this in a containerized world.
Configuration Management vs Provisioning
Two different problems often confused:
Provisioning — creating infrastructure (servers, networks, databases). Terraform's job.
Configuration management — configuring software ON those servers (installing packages, setting up users, deploying apps). Ansible's job.
In the container era, configuration management's role has shrunk. If you build a Docker image, the configuration is baked in — you don't need Ansible to install Node.js on a running server.
But CM is still useful for:
• Configuring servers BEFORE they run containers (initial Docker setup, K8s nodes, basic hardening)
• Bare-metal or VM workloads
• Network appliances (Ansible has modules for routers, switches)
• On-premises systems
• Patching and maintenance of fleets
• One-off ad-hoc tasks across many servers
This lesson focuses on Ansible — the most popular CM tool by far. Chef, Puppet, and SaltStack are alternatives but Ansible has won the market in 2026.
Ansible Mental Model
Ansible is agentless. You don't install anything on the target servers. Ansible connects via SSH, runs commands, then disconnects.
Three concepts:
Inventory — list of hosts. Can be static (a file) or dynamic (queried from cloud APIs).
# inventory.ini
[web]
web1.example.com
web2.example.com
web3.example.com
[db]
db.example.com
[all:vars]
ansible_user=deploy
ansible_python_interpreter=/usr/bin/python3
Playbook — YAML file describing what to do. Tasks run in order on each host.
# site.yml
- name: Configure web servers
hosts: web
become: true # use sudo
tasks:
- name: Install nginx
apt:
name: nginx
state: present
update_cache: true
- name: Start nginx
systemd:
name: nginx
state: started
enabled: true
- name: Deploy nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: reload nginx
handlers:
- name: reload nginx
systemd:
name: nginx
state: reloaded
Run:
ansible-playbook -i inventory.ini site.yml
Modules — the verbs (apt, systemd, template, copy, git, user, cron...). Ansible has thousands. Anything you can do on a Linux server has a module.
Idempotence — Ansible modules are idempotent by design. Running the playbook 10 times has the same effect as running it once. The apt module checks if nginx is already installed before installing. The systemd module checks if it's already running. This is what makes CM safe to re-run.
Variables and Templates
Real playbooks need configuration. Ansible has rich variable support.
Variables in inventory:
[web]
web1.example.com http_port=80
web2.example.com http_port=80
web3.example.com http_port=8080
Or in a separate file:
# group_vars/web.yml
http_port: 80
worker_processes: auto
log_level: warn
Use them in tasks:
- name: Render config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
vars:
server_name: "{{ inventory_hostname }}"
Jinja2 templates:
# templates/nginx.conf.j2
worker_processes {{ worker_processes }};
events {
worker_connections 1024;
}
http {
server {
listen {{ http_port }};
server_name {{ server_name }};
{% for backend in backend_servers %}
upstream backend {
server {{ backend }};
}
{% endfor %}
location / {
proxy_pass http://backend;
}
}
}
Group variables (group_vars/all.yml, group_vars/web.yml) and host variables (host_vars/web1.example.com.yml) provide layered config. Specific overrides general.
Roles — Reusable Bundles
A role is a directory bundling tasks, templates, files, handlers, and variables for one purpose.
roles/
nginx/
tasks/main.yml
handlers/main.yml
templates/nginx.conf.j2
files/some_static_file
vars/main.yml # role-specific variables
defaults/main.yml # default values (lower precedence)
meta/main.yml # role metadata, dependencies
Use a role:
- name: Configure web tier
hosts: web
roles:
- common # base hardening, monitoring agents
- nginx
- app_deployment
Galaxy — Ansible's role registry: galaxy.ansible.com. Use community roles for things like:
- geerlingguy.docker — install Docker
- geerlingguy.postgresql — install Postgres
- nginxinc.nginx — official Nginx role
ansible-galaxy install geerlingguy.docker
Then in your playbook:
roles:
- geerlingguy.docker
Build your own roles for things specific to your org: hardening standards, monitoring agents, app deployment patterns.
Ansible vs Terraform — When Each
They solve different problems:
Terraform creates infrastructure: "give me 10 EC2 instances with this VPC and these security groups."
Ansible configures infrastructure: "on those 10 EC2 instances, install Docker, configure logging, deploy the app."
Common pattern:
1. Terraform creates servers
2. Cloud-init or user-data runs an initial bootstrap
3. Ansible takes over for ongoing configuration
4. Or: Terraform → Packer (builds AMI with Ansible inside) → done. The AMI is fully configured before launch.
In 2026:
• If you're on Kubernetes/serverless: Ansible mostly disappears. Container images are your "configuration."
• If you have VMs: Terraform + Ansible (or Terraform + Packer + Ansible).
• If you have bare metal or specific compliance needs: Ansible remains essential.
Honest take: many teams that started with Ansible 5+ years ago are now reducing its role as containers eat the workload. New teams skip Ansible and use containers + a thin shell of Terraform. But for sysadmin work — especially on existing infrastructure — Ansible is still hugely productive.
The next lessons cover observability — once your infrastructure is provisioned and configured, you need to know what's actually happening on it.
⁂ Back to all modules