The Replacement ■ Episode 07
Ansible. Puppet. Chef. Salt. Terraform. All solving the same problem: how do I run the same commands on multiple machines?
Here is a thought: SSH already does that.
The Loop
#!/bin/sh
SERVERS="web1 web2 db1 db2 cache1"
for host in $SERVERS; do
ssh "$host" 'pkg upgrade -y && service nginx reload'
done
That is configuration management. Five lines. No framework. No YAML. No Python runtime on the target machine. Just a shell, a loop, and the protocol that has been shipping with every Unix system since 1999.
Idempotency
The usual objection arrives quickly. "But what about idempotency?" A fair question. Here is a fair answer:
ssh web1 'grep -q "worker_processes 4" /etc/nginx/nginx.conf || \
sed -i "" "s/worker_processes.*/worker_processes 4;/" /etc/nginx/nginx.conf && \
service nginx reload'
Only changes if needed. Only reloads if changed. Idempotent.
No module system required. The tools are grep,
sed, and a conditional. Available since the
1970s.
State Management
#!/bin/sh
# deploy.sh
set -e
for host in $SERVERS; do
echo "=== $host ==="
scp configs/nginx.conf "$host":/etc/nginx/nginx.conf
scp configs/rc.conf "$host":/etc/rc.conf
ssh "$host" 'service nginx configtest && service nginx reload'
done
The state lives in your git repository. The configs/
folder. Version controlled. Diffable. Auditable. No inventory
plugin. No state file. No backend configuration. Git is the
backend.
Secrets
# Encrypt with age (single binary, no GPG complexity)
age -r age1ql3z7hjy... secret.txt > secret.txt.age
# Decrypt on target
scp secret.txt.age "$host":~
ssh "$host" 'age -d -i ~/.age/key.txt secret.txt.age > /etc/secret.txt'
age, written by Filippo Valsorda in 2019. One binary. No key servers. No web of trust. No GPG configuration odyssey. Or use ssh-agent forwarding. The tooling exists.
Parallel Execution
#!/bin/sh
for host in $SERVERS; do
ssh "$host" 'pkg upgrade -y' &
done
wait
echo "All done."
Background jobs. The ampersand. Available since 1971. No task runner. No worker pool library. The shell has had concurrency longer than most programming languages have existed.
What Ansible Actually Gives You
YAML syntax (a debatable improvement over shell). Inventory files (a text file with hostnames). Modules (shell commands with extra steps). Playbooks (shell scripts with extra steps). Galaxy (dependencies with extra steps).
What you give Ansible: Python on every target machine. A domain-specific language to learn. YAML indentation errors to debug. Tasks that run every time despite being marked "changed." Module version compatibility issues between releases.
The Honest Caveat
"But we have 500 servers across 3 continents!" Then you need Ansible. Possibly Terraform. Maybe even Kubernetes. Terribly sorry, this article is not for you.
But if you are managing five servers? Ten? A small startup?
A personal lab? You do not need configuration management
software. You need ssh, a for loop,
and configs in git.
The tools are not complicated. We just convinced ourselves they must be.