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

  1. Base system: users and SSH keys, sudo rules, timezone and NTP, automatic updates, firewall, Fail2ban. See Linux server hardening checklist.
  2. Services: Nginx/Apache, PHP-FPM, database settings, Redis, Docker.
  3. Application deployment: code, configuration, systemd units, cron jobs.
  4. 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

  1. Keep a Git repository with your playbooks, templates and an inventory.
  2. Start by codifying the base setup you repeat for every new server.
  3. Add one service at a time, as you touch it.
  4. 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.
  5. 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.

  • DevOps
  • Ansible
  • Automation