Skip to main content
RunBook Academy

AnsibleIII · Installing and Designing the ControllerController design

Whose machine runs production change?

Intermediate⏱ ~19 minbashgit

What you'll learn

  • Compare laptop, jump host, CI runner and platform controllers on attribution, reproducibility and exposure
  • Explain why "it works on my controller" is a governance symptom rather than a tooling one
  • Identify what an interactive workstation adds to the attack surface of a production controller
  • Design a two-controller split that keeps exploration cheap and production change reviewable

Prerequisites

Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11

Not yet marked complete on this device.

Nobody decides where the controller lives. It accumulates.

Somebody installs Ansible on their laptop to try something. It works, so they use it for the next thing. Six months later the laptop is the only machine that can deploy the application, the SSH key that reaches every production server is in that person’s home directory, and the estate has made a governance decision that nobody made.

This lesson is about making it on purpose.

Four options

LaptopShared jump hostCI runnerAutomation platform
Who ran itThe owner, unattributedA service account — attribution needs careThe pipeline, attributed to a commitThe platform, attributed to a job and a user
ReproducibleOnly for that personYes, if built from the repositoryYes, by constructionYes
Reviewed before runningOnly by conventionOnly by conventionEnforced by the mergeEnforced by the job definition
Where the fleet key livesNext to a browserOn a hardened hostIn the CI secret storeIn the platform’s credential store
Works during a network incidentSometimesIf you can reach itRarelyRarely
Setup costNoneLowMediumHigh

None of these rows is decisive on its own. Read them together and a pattern appears: the options get stronger on governance and weaker on availability as you move right, and the cost rises the whole way.

The laptop

It is the right controller for exactly one thing: exploration. Writing a new role, running --check against staging, debugging an inventory, learning. The feedback loop is short and nothing else can match it.

It is a poor controller for production change, for four reasons that have nothing to do with the engineer’s competence.

Attribution disappears. A run from a laptop leaves a record on the managed hosts — sudo entries, changed files, a service restart — and nothing that connects the change to a reviewed intention. Six weeks later the question is “why is this host different”, and the answer is in somebody’s shell history, if it is anywhere.

Reproducibility is personal. The laptop’s controller environment is the one they built. Their collection versions, their ~/.ansible.cfg, their SSH config with a ProxyJump nobody else has. It works, and it works only there.

Availability is a property of a person’s day. The deployment happens when the laptop is open, awake and on the VPN. This is fine until it is 2 a.m. or the person is on a plane.

The exposure is the point. A laptop runs a browser, a mail client, a chat application and whatever a developer needed last Tuesday. Every one of those is an execution path for content the owner did not write. On that same machine sits a passphraseless key with root on every production host.

The shared jump host

A dedicated machine, a service account, the repository deployed from Git, the venv built from requirements.txt. Everyone runs the same Ansible because there is only one.

This solves reproducibility and most of the exposure problem. It leaves two things open.

Attribution needs deliberate work. If four engineers sudo -u svc-ansible and run playbooks, the managed hosts see one identity. Shell audit on the controller, log_path in ansible.cfg, and a convention of running through a wrapper that records who invoked it are the mitigations — and all three are things you have to add, not things you get.

Review is still a convention. Nothing stops an engineer editing the checkout in place and running it. The filesystem layout from the previous lesson is what makes that hard: the checkout is owned by root and deployed, not writable by the people who use it.

Read-only / Safewhat a shared controller should be able to tell you
ansible --version | head -2
ansible-galaxy collection list
ansible-config dump --only-changed
Read-only / Safethe third one, on a correctly configured controller
$ ansible-config dump --only-changed
CONFIG_FILE() = /opt/estate/ansible.cfg
DEFAULT_HOST_LIST(/opt/estate/ansible.cfg) = ['/opt/estate/inventory/production']
DEFAULT_LOG_PATH(/opt/estate/ansible.cfg) = /var/log/ansible/ansible.log
DEFAULT_VAULT_PASSWORD_FILE(/opt/estate/ansible.cfg) = /etc/ansible/vault-pass
HOST_KEY_CHECKING(/opt/estate/ansible.cfg) = True

GALAXY_SERVERS:

Illustrative output

Note what that output gives you for free: each setting is annotated with where it came from. A value attributed to a file you did not expect is the fastest possible diagnosis of a configuration surprise.

The CI runner

The strongest governance position, because review stops being a convention and becomes a mechanism. Change reaches production by being merged, and the runner has credentials that the humans do not.

What it buys:

  • Every change is attributable to a commit, an author and an approval.
  • The environment is built from the repository on every run, so the two-machine test is executed continuously rather than annually.
  • Credentials live in the CI secret store rather than in a home directory, and can be scoped per pipeline.

What it costs, and these are real:

  • Feedback is slow. A five-second change becomes a pipeline.
  • Debugging is awkward. You cannot easily attach to a failing run, and reproducing it locally means reproducing the runner.
  • It is a dependency during an incident. If the outage involves the network, the CI system, or the identity provider the CI system authenticates against, your remediation tool is behind the failure.

The automation platform

AWX, Ansible Automation Platform, or an equivalent. It adds job templates, a credential store, RBAC, scheduling, a surveyed interface for people who should not have a shell, and a job history that is a real audit trail.

It is the right answer for an organisation where many people need to run a limited set of automations, and where “who may run what against which inventory” is a question with a formal answer. It is a heavyweight answer for a team of four, where the platform becomes another system to patch, back up and recover.

The course does not treat the platform as the destination that everyone should reach. It treats it as one point on the same axis: further right, stronger governance, higher cost.

What most estates should actually do

Two controllers, and the split is by purpose rather than by person.

A personal controller — laptop or a small VM — with the repository checked out, credentials that reach staging only, and no production inventory present. Explore here. Run --check here. Break things here. The blast radius is bounded because production is not in the file.

A production controller — a jump host, a CI runner, or both — with the production inventory, the production credentials, and a checkout nobody edits in place.

The important detail is that the boundary is enforced by which inventory exists on which machine, not by discipline. An engineer on the personal controller cannot accidentally target production, because production is not there to be targeted. This is the same argument the inventory design part makes at fleet scale, arriving early because it is also the answer to where the controller lives.

Knowledge check

Knowledge check · 4 questions

  1. Q1. What is the strongest argument against running production change from an engineer laptop?

  2. Q2. A CI runner is the strongest governance position for a production controller. Which of these are genuine costs of that choice? Select all that apply.

  3. Q3. On a shared controller, three machine-level checks - ansible --version, the collection list and the config dump - are enough to explain any "works on my controller" report.

  4. Q4. A team splits into a personal controller for exploration and a production controller for change. What actually enforces the boundary?

Passing score: 75%. Answers are checked in this browser.