If you configure servers by hand, each one slowly becomes a unique “snowflake”: nobody remembers exactly what was installed or changed, and rebuilding it after a failure takes days. Configuration management means describing a server’s desired setup in code, so you can rebuild it, or create ten more identical ones, in minutes.
The options
| Approach | How it works | Best for |
|---|---|---|
| Shell scripts | Run commands in order | One-off setups, very small fleets |
| cloud-init | First-boot configuration (users, packages, files) | Initial provisioning of new VMs |
| Ansible | Agentless; YAML playbooks over SSH | Most small and medium setups |
| Puppet / Chef | Agent on each server pulls config from a central server | Large fleets, long-lived enterprise environments |
| SaltStack | Agent-based (or SSH), fast, event-driven | Large fleets, remote execution |
| NixOS / Guix | The whole OS defined declaratively | Fully reproducible systems |
| Containers | App + dependencies in an image | Application layer, on top of a minimal host |
Key idea: idempotency
A good configuration tool describes the desired state (“nginx installed, this config file present, service running”), and only changes what differs. Running it twice changes nothing the second time. Shell scripts often aren’t idempotent (echo … >> file appends again on every run), which is why dedicated tools exist.
Ansible: the usual choice for a few servers
- No agent: only SSH and Python on the server.
- Readable YAML playbooks, with a huge collection of modules (packages, files, users, services, firewall, Docker).
- Works from your laptop or a CI pipeline.
# site.yml
- hosts: web
become: true
tasks:
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Deploy site config
ansible.builtin.template:
src: example.com.conf.j2
dest: /etc/nginx/sites-available/example.com
notify: reload nginx
- name: Enable site
ansible.builtin.file:
src: /etc/nginx/sites-available/example.com
dest: /etc/nginx/sites-enabled/example.com
state: link
notify: reload nginx
handlers:
- name: reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
ansible-playbook -i inventory.ini site.yml --check --diff # dry run
ansible-playbook -i inventory.ini site.yml
See install Ansible, Ansible playbooks, and Ansible inventory.
cloud-init for first boot
cloud-init applies configuration the first time a VM boots: create users, add SSH keys, install packages, write files, run commands. A common pattern is cloud-init for the bare minimum (a user, SSH key, Python), then Ansible for everything else. See cloud-init explained.
What to put under management
- Base system: users and SSH keys, sudo rules, timezone and NTP, automatic updates, firewall, Fail2ban. See Linux server hardening checklist.
- Services: Nginx/Apache, PHP-FPM, database settings, Redis, Docker.
- Application deployment: code, configuration, systemd units, cron jobs.
- Monitoring and backups: agents, backup scripts and schedules.
Data (databases, uploads) isn’t configuration. It’s restored from backups.
Secrets
Don’t commit passwords and keys in plain text. Options: Ansible Vault (encrypted variables in the repo), SOPS, a secrets manager (Vault, Bitwarden Secrets Manager), or CI secrets injected at runtime. See secrets management.
A practical path for a few VPS servers
- Keep a Git repository with your playbooks, templates and an inventory.
- Start by codifying the base setup you repeat for every new server.
- Add one service at a time, as you touch it.
- Rebuild a test VPS from scratch occasionally, to prove it works (reinstall the OS from your control panel, run the playbook). See reinstall the OS.
- Track manual drift on existing servers with etckeeper. See etckeeper.
Quick reference
| Need | Tool |
|---|---|
| First boot setup | cloud-init |
| Ongoing config, few servers | Ansible |
| Large fleet with agents | Puppet / Chef / Salt |
| App packaging | Docker / containers |
| Record manual changes | etckeeper |
| Encrypted secrets in repo | Ansible Vault / SOPS |
Related: CI/CD pipeline explained and VPS for developers.