Home
DevOps & Cloud Engineering / Lesson 22 — Configuration Management — Ansible

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).

Ini
# 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.

YAML
# 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:

Bash
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:

Ini
[web]
web1.example.com http_port=80
web2.example.com http_port=80
web3.example.com http_port=8080

Or in a separate file:

YAML
# group_vars/web.yml
http_port: 80
worker_processes: auto
log_level: warn

Use them in tasks:

YAML
- name: Render config
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  vars:
    server_name: "{{ inventory_hostname }}"

Jinja2 templates:

Jinja
# 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.

Text
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:

YAML
- 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

Bash
ansible-galaxy install geerlingguy.docker

Then in your playbook:

YAML
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