Building on our Git fundamentals, this week we expand on Ansible — a simple yet powerful automation engine for configuration management, orchestration, and especially monthly server patching.
If you’ve ever clicked “Update All” at 2 a.m. on a jump box while whispering “please don’t brick prod,” this post is for you. We’ll turn patch Tuesday (or “whenever we have time” Wednesday) into a tidy, repeatable playbook you can run with confidence—and a cup of coffee that stays hot.
My goal isn’t to impress you with YAML acrobatics. It’s to give you a workflow you can actually use this month, with just enough safety rails to sleep fine afterward.
Table of Contents
Open Table of Contents
Why Ansible?
Ansible is agentless, meaning no special software needs to run on managed nodes — just SSH and Python. It’s popular in DevOps for:
- Configuration Management: Install packages, manage files, start/stop services.
- Orchestration: Coordinate changes across multiple servers.
- Provisioning: Deploy infrastructure resources.
- Application Deployment: Automate complex multi-tier deployments.
- Patching: Safely apply OS and package updates on a predictable cadence.
Why beginners love it (and veterans keep it):
- Human-readable YAML. No mystery meat.
- Huge module ecosystem.
- Easy dry-runs (
--check) so you can rehearse before the real thing.
Real talk: I used to keep a spreadsheet of servers to patch. It worked—right up until it didn’t. Ansible replaced that fragile routine with something boring, reliable, and fast. Boring is good when uptime matters.
Ansible Architecture
Ansible has a control node and managed nodes.
- Control Node: Where you run
ansiblecommands. - Managed Nodes: Servers you manage via SSH.
Key Components:
- Inventory: List of managed nodes.
- Modules: Units of work (e.g.,
apt,dnf,service,reboot). - Playbooks: YAML files describing tasks.
- Roles: Structured, reusable playbooks.
Mental model:
Control Node
|
+--(SSH)--> Managed Node(s)
|
+-- Python executes modules
You talk to hosts over SSH; Ansible drops tiny, purpose-built tasks on the other side. No long-running daemons, no agents to babysit.
Installation and Setup
Two-minute install:
pip install ansible
# optional best practices:
pip install ansible-lint
On Windows: use WSL2 (Ubuntu recommended) and run the same commands.
Verify:
ansible --version
If that worked, you’re already 80% of the way to useful automation.
Inventory Files
Ansible uses an inventory to know which hosts to manage.
Example (inventory.ini):
[web]
web1.example.com
web2.example.com
[db]
db1.example.com
[all:vars]
ansible_user=ubuntu
Pro tip: Start small. Put two or three non-prod hosts in a stage group and practice there first.
Note: Ensure SSH access and keys are configured in advance; all examples assume working SSH.
Smoke test:
ansible -i inventory.ini all -m ping
If you see “pong,” you’re in business.
Quick Patching with Ad-Hoc Commands
When you just need to get it done.
Debian/Ubuntu:
# Update package index and perform a safe full upgrade
ansible -i inventory.ini all -m apt -a "update_cache=yes upgrade=dist" --become
# Dry run (no changes), show what would happen
ansible -i inventory.ini all -m apt -a "update_cache=yes upgrade=dist" --become --check
RHEL/CentOS/Alma/Rocky:
# Bring all packages to latest
ansible -i inventory.ini all -m dnf -a "name='*' state=latest" --become
# Dry run (approximation; check mode support varies by module)
ansible -i inventory.ini all -m dnf -a "name='*' state=latest" --become --check
Need a reboot?
ansible -i inventory.ini web -m reboot --become
Why this matters: Ad-hoc commands are your “grab a wrench” toolkit—perfect for small fleets and emergency fixes.
Playbooks and YAML Basics
Playbooks are declarative: you describe the end state; Ansible figures out the steps.
Example: a minimal patching playbook:
---
- name: Minimal patch for Debian/Ubuntu
hosts: all
become: yes
tasks:
- name: Update and upgrade
apt:
update_cache: yes
upgrade: dist
when: ansible_os_family == "Debian"
- name: Update to latest on RHEL-like
dnf:
name: "*"
state: latest
when: ansible_pkg_mgr == "dnf"
Run:
ansible-playbook -i inventory.ini site.yml
Think of playbooks as saved, repeatable commands—with documentation baked in.
Safe, Idempotent Patching (with Reboots)
Idempotence means running the same playbook twice won’t cause surprises. For patching, we add seatbelts and airbags:
- Rolling updates so we don’t take everything down at once.
- Conditional reboots—only when necessary.
- Pre-flight checks to catch silly issues (like low disk space).
- Dry-runs so you can rehearse.
Serial is your friend: it’s like taking servers off the shelf one at a time.
---
- name: Monthly Patching (Rolling + Reboots)
hosts: all
become: yes
gather_facts: yes
serial: 25% # roll through 25% of hosts at a time
any_errors_fatal: true
pre_tasks:
- name: Ensure hosts are reachable
wait_for_connection:
timeout: 60
- name: Check free disk space (>= 1GB recommended)
shell: df -Pk / | awk 'NR==2{print $4}'
register: root_free_kb
changed_when: false
- name: Fail if insufficient disk space for updates
fail:
msg: "Not enough disk space for patching."
when: root_free_kb.stdout | int < 1048576
tasks:
- name: Debian/Ubuntu - update and dist-upgrade
apt:
update_cache: yes
upgrade: dist
register: debian_updates
when: ansible_os_family == "Debian"
notify: maybe_reboot
- name: RHEL-like - update all packages to latest
dnf:
name: "*"
state: latest
register: rhel_updates
when: ansible_pkg_mgr == "dnf"
notify: maybe_reboot
- name: Debian/Ubuntu - check reboot-required file
stat:
path: /var/run/reboot-required
register: reboot_flag
when: ansible_os_family == "Debian"
- name: RHEL-like - check if reboot is required (needs-restarting)
shell: needs-restarting -r || true
register: needs_reboot
changed_when: false
failed_when: false
when: ansible_os_family == "RedHat"
- name: Set reboot_needed fact
set_fact:
reboot_needed: >-
{{
(ansible_os_family == "Debian" and reboot_flag.stat.exists | default(false))
or
(ansible_os_family == "RedHat" and (needs_reboot.rc | default(0)) == 1)
}}
handlers:
- name: maybe_reboot
when: reboot_needed | default(false)
listen: maybe_reboot
block:
- name: Reboot and wait
reboot:
reboot_timeout: 900
- name: Verify system is back and stable
wait_for_connection:
timeout: 120
post_tasks:
- name: Collect kernel and uptime for report
shell: "uname -r && uptime -p"
register: system_report
changed_when: false
- name: Show summary
debug:
msg:
- "Host: {{ inventory_hostname }}"
- "Rebooted: {{ reboot_needed | default(false) }}"
- "Kernel/Uptime: {{ system_report.stdout_lines }}"
Safety net: run with --check --diff first to preview changes.
Hands-On Lab: Monthly Patch Run
Goal: Safely patch all servers with rolling updates and conditional reboots.
- Step 0: Make coffee. You’ll have time to drink it.
- Step 1: Create
inventory.iniwith host groups. - Step 2: Save the Monthly Patching playbook as
patch.yml. - Step 3: Dry run:
ansible-playbook -i inventory.ini patch.yml --check --diff
- Step 4: Execute:
ansible-playbook -i inventory.ini patch.yml
- Step 5: If you’re nervous, limit to one group or host:
ansible-playbook -i inventory.ini patch.yml --limit web
If something fails, Ansible stops the batch (thanks, serial). Fix the issue, re-run, and carry on.
Scheduling and Reporting
Make it boring—in the best way.
Options:
- Cron: Run
ansible-playbookmonthly from a trusted control node; log output to a file. - AWX/Ansible Automation Platform: Click “Template,” pick your project and inventory, set a Schedule, and enjoy a history of runs with notifications.
- Reporting: Email or Slack a summary by parsing
debugresults or using callback plugins.
Example cron entry:
0 2 1 * * /usr/local/bin/ansible-playbook -i /etc/ansible/inventory.ini /opt/patch.yml >> /var/log/ansible/patch-$(date +\%Y-\%m).log 2>&1
Runbooks age well when they leave breadcrumbs. Keep logs, keep notes, keep your future self happy.
Q&A
Q: Do I need root access?
A: Only for privileged changes — use --become. Keep sudo tight and audited.
Q: Can Ansible manage Windows patching?
A: Yes. Use win_updates over WinRM and the same rolling strategy. Same playbook pattern, different modules.
Q: What about rollback? A: For OS updates, favor snapshots/AMIs or VM backups before patching. Package managers offer limited rollback; snapshots are your safety rope.
Q: How do I avoid downtime?
A: Use serial, health checks, and maintenance windows. Start with a small --limit, then expand. Practice in non-prod first.
Closing thought: Good ops feels calm. With a small investment in Ansible, patch night turns from adrenaline to routine. That’s the win.