Ansible
Ansible automates server and service configuration with YAML playbooks and idempotent modules. Python teams use it for bootstrapping hosts, deploying apps, and enforcing baseline OS hardening without maintaining imperative shell scripts.
Search across all documentation pages
Ansible automates server and service configuration with YAML playbooks and idempotent modules. Python teams use it for bootstrapping hosts, deploying apps, and enforcing baseline OS hardening without maintaining imperative shell scripts.
Quick-reference recipe card - copy-paste ready.
# playbook.yml
- hosts: web
become: true
tasks:
- name: Ensure app directory exists
ansible.builtin.file:
path: /opt/myapp
state: directory
mode: "0755"
- name: Deploy config
ansible.builtin.template:
src: templates/app.env.j2
dest: /opt/myapp/.env
mode: "0600"ansible-playbook -i inventory.ini playbook.yml --check
ansible-playbook -i inventory.ini playbook.ymlWhen to reach for this:
changed=0 idempotency reportsA minimal project with inventory, role, and Jinja2 template deployed via ansible-playbook.
# inventory.ini
[web]
web1 ansible_host=127.0.0.1 ansible_connection=local# site.yml
- hosts: web
roles:
- myapp# roles/myapp/tasks/main.yml
- name: Create app user
ansible.builtin.user:
name: myapp
system: true
shell: /usr/sbin/nologin
- name: Install Python venv package (Debian family)
ansible.builtin.apt:
name: python3-venv
state: present
become: true
- name: Deploy environment file
ansible.builtin.template:
src: app.env.j2
dest: /etc/myapp.env
owner: root
group: myapp
mode: "0640"# roles/myapp/templates/app.env.j2
APP_ENV={{ app_env | default('dev') }}
LOG_LEVEL={{ log_level | default('info') }}ansible-playbook -i inventory.ini site.yml -e app_env=prod -e log_level=warningWhat this demonstrates:
ansible_connection=local runs on your machine for demosansible.builtin.template renders Jinja2 with variables from -e or group_varschanged only when state actually differsgather_facts: false--ask-vault-pass or key file| Concept | Purpose |
|---|---|
| Inventory | Host groups and connection vars |
| Playbook | Ordered plays and tasks |
| Role | Reusable task/template bundle |
| Module | Idempotent unit of work (apt, file, service) |
| Handler | Restart service only when config changed |
# Call Ansible from Python via ansible-runner (optional orchestration)
import ansible_runner
result = ansible_runner.run(
playbook="site.yml",
inventory="inventory.ini",
extravars={"app_env": "staging"},
)
assert result.status == "successful", result.stdout.read()shell for everything - loses idempotency and changed reporting. Fix: find the matching ansible.builtin.* module first.ansible-vault encrypt_string or external secret store lookup plugins.--check first - surprises on shared hosts. Fix: dry-run in CI and require approval for prod plays.ansible-galaxy collection install pulls latest and breaks playbooks. Fix: pin versions in requirements.yml.| Alternative | Use When | Don't Use When |
|---|---|---|
| Pulumi/Terraform | Cloud resource lifecycle | Only need OS package config on existing VMs |
| Fabric/paramiko scripts | One-off remote commands | You need idempotent convergence reports |
| cloud-init | First-boot only on new instances | Ongoing config drift correction |
| Salt/Chef | Large fleet with agents acceptable | You want agentless SSH simplicity |
Modules compare desired vs actual state and report changed counts. Shell scripts rerun blindly and hide drift.
Yes - use amazon.aws collection modules (ec2_instance, s3_bucket). For complex VPC topologies, pair Terraform/Pulumi for cloud and Ansible for host config.
A play targets host groups and lists tasks or roles. A role is a reusable folder of tasks, handlers, templates, and defaults included by plays.
Use ansible_connection=local in inventory or Molecule with Docker/Podman drivers to converge ephemeral test instances.
Handlers execute once at the end of the play, only if a task notified them and reported changed. Use for service restarts after config updates.
Use group_vars/<group>.yml, host_vars/, or -e key=value at runtime. Keep structure identical across envs, change values only.
Most modules need Python on the remote host (preinstalled on mainstream Linux). Raw command/shell modules work without it but lose idempotency.
Enable SSH pipelining, use strategy: free for independent hosts, and limit fact gathering with gather_subset when full facts are unnecessary.
Yes - install ansible into your project venv with uv pip install ansible and invoke ansible-playbook from that environment for reproducible versions.
--check simulates changes without applying them. Pair with --diff to preview file content changes before prod runs.
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026