Skip to content

Ansible + Git

5 min readGit for DevOps & Infrastructure · Ansible

Ansible is the tool in this pillar with the shortest distance between a merged commit and a changed production host.

Terraform has a plan you can read. Kubernetes has a controller that converges gradually and can be paused. Ansible connects to machines and does what the playbook says, in order, now. That immediacy is why it is useful and why the repository engineering around it matters more than people expect.

Start with version controlling Ansible

    Validation and execution are different activities and must not share a workflow.

    Everything in this cluster follows from that. Linting a playbook is safe: it reads files and reports. Running a playbook is not safe: it connects to hosts and changes them. A CI pipeline that does the first is a quality gate; one that does the second is a deployment system with production credentials, triggered by whatever somebody pushed.

    The line shows up as concrete rules:

    CI validates. It does not connect to managed hosts. Syntax check, ansible-lint, role tests against disposable containers. Nothing in that list needs an SSH key to a real server.

    Execution against real inventories is a deliberate, approved action. Environment protection rules, manual approval, a specific person accountable — covered in Ansible + GitHub Actions.

    --check before the real run, wherever the modules support it honestly. It is the closest thing Ansible has to a plan, and its limits are worth knowing.

    In GitNot in Git
    Playbooks and rolesVault passwords, in any form
    requirements.ymlPrivate SSH keys
    Inventory structure and groups.retry files
    group_vars / host_vars with non-secret valuesDownloaded collections and roles
    Encrypted vault files, with keys held elsewhereReal credentials, encoded or otherwise
    ansible.cfg, .ansible-lint, CI workflowsGenerated fact caches

    Two rows deserve emphasis now and get a full lesson later.

    Downloaded content does not belong in the repository. requirements.yml declares what you depend on; ansible-galaxy install fetches it. Committing roles/ and collections/ produces a repository where nobody can tell what is yours.

    An encrypted vault file may be committed. Its password may not. Ansible Vault and Git covers why that distinction changes the risk model without eliminating your key-management responsibilities.

    A playbook describes what to do. An inventory describes what to do it to, and that is the file that leaks operational detail: hostnames, network layout, environment names, sometimes credentials in variables.

    Two consequences run through the cluster:

    Structure inventories so production is hard to hit by accident. Separate files, separate directories, explicit --limit, and never a default that targets everything.

    Examples in this cluster use reserved names. example.com, 192.0.2.0/24 and the other RFC 5737 documentation ranges. Copying a tutorial’s plausible-looking internal hostname into a real inventory is a specific, recorded way people cause incidents.

    Ansible’s testing story is better than its reputation, and it is layered.

    Syntax checkansible-playbook --syntax-check. Fast, catches structural errors, proves nothing about behaviour.

    Lintansible-lint. Catches deprecated modules, missing names, idempotency risks and a large set of real defects. The highest value per second of runtime in the whole cluster.

    Role testsMolecule. Creates a disposable target, converges the role against it, verifies the result, destroys it. This is where you find out whether the role actually works.

    Idempotency — running twice and asserting the second run changes nothing. Ansible’s central promise, and the thing most broken roles fail.

    None of that requires access to a production host, which is the point.

    Ansible sits differently from the other tools here, and the differences explain most of the design decisions in this cluster.

    It is imperative in a pillar full of declarative tools. A playbook is an ordered list of tasks. Terraform builds a graph; Kubernetes converges continuously; Ansible runs steps in sequence. That makes it excellent for operations with genuine ordering requirements and poor at continuous reconciliation.

    It has no state file. Nothing records what Ansible previously did. Idempotent modules re-derive the current situation each run by inspecting the host. That removes an entire category of problem — no state to lose, corrupt, or accidentally commit — and creates another: no plan, no drift report, and no reliable answer to “what would this change?”

    It is push-based by design. The control node connects outward to managed hosts. There is no agent pulling desired state, which means Ansible does not satisfy the GitOps principles and should not be described as GitOps regardless of how the playbooks are stored.

    It runs against hosts, not APIs. Ansible has excellent cloud and Kubernetes modules, but its centre of gravity is configuring machines. Where the same job could be done by Terraform or a Kubernetes controller, that is usually the better fit — and using Ansible for provisioning it does not naturally model is a common source of unmaintainable repositories.

    The practical upshot: use Ansible where ordered, imperative host configuration is the actual requirement, and resist the temptation to grow it into a general-purpose infrastructure tool because it is the one your team already knows.

    Ansible repositories degrade in a recognisable way, and the fix depends on where you are on the curve.

    Small — a handful of playbooks, one inventory, roles inline. Perfectly fine, and most teams should stay here longer than they do.

    Medium — several inventories, roles factored out, group_vars doing real work. This is where repository structure starts to matter and where lint and Molecule earn their keep.

    Large — roles and collections extracted into their own versioned repositories, consumed through requirements.yml at pinned versions. Slower to change deliberately, which is the point.

    The migration people get wrong is jumping to the third stage too early: a dozen role repositories each with three commits, and a requirements.yml nobody updates. Extract a role when two consumers genuinely need it at different versions, not when it feels tidier.

    Pillar 4 owns the CI workflow mechanics. This cluster owns the repository those workflows validate and the boundary between validating and executing.

    Pillar 5 owns secret handling depth. This cluster applies it to the specific problem of Vault, inventories and the key you must not commit.

    Lesson 1 — Version Controlling Ansible