# pyinfra: Ansible's pitch, Python's body

> pyinfra is an MIT-licensed Python tool that turns Python code into shell commands and runs them over SSH, Docker, local machine and Terraform-connected targets, from one host to thousands. It offers idempotent operations with diffs and dry runs, realtime debugging at triple verbosity, and gevent-based parallel execution, with the README's own summary being ansible but Python instead of YAML, and a lot faster.

**pyinfra-dev/pyinfra** — 🔧 pyinfra turns Python code into shell commands and runs them on your servers. Execute ad-hoc commands and write declarative operations. Target SSH servers, local machine and Docker containers. Fast and scales from one server to thousands.

- Repository: https://github.com/pyinfra-dev/pyinfra
- Website: https://pyinfra.com
- Stars: 6,022 · Forks: 552
- Language: Python
- License: MIT
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/pyinfra-dev-pyinfra

## The elevator pitch, in the project's own words

The README compresses pyinfra into one sentence, think ansible but Python instead of YAML, and a lot faster, and every design feature expands that claim. Execution is super fast over thousands of hosts with predictable performance, debugging is instant with realtime stdin, stdout and stderr output at triple verbosity, operations are idempotent and support diffs and dry runs before any change lands, and the whole Python package ecosystem is available for extension because the configuration language is the language itself. Execution is agentless, anything with shell access is a target, and connectors integrate Docker, Terraform, Vagrant and more. The author is Nick, known as Fizzadar, the license is MIT, and the project lives at pyinfra.com with documentation at docs.pyinfra.com and a Matrix room for chat.

## uv tool install, then exec

The recommended install runs through uv, the fast Python toolchain manager:

```sh
uv tool install pyinfra
```

Immediately after, commands execute on remote hosts over SSH:

```sh
pyinfra my-server.net exec -- echo "hello world"
```

The connector system arrives in the same breath, with the at-sign prefix routing the same command to different targets, a Docker container or the local machine:

```sh
pyinfra @docker/ubuntu exec -- echo "Hello world"
pyinfra @local exec -- echo "Hello world"
```

The three lines teach the whole mental model, one CLI, one verb, targets distinguished by prefix, so the gap between installing the tool and running something real on something real is under a minute, and the docs list further connectors on their own page.

## From a CLI flag to a Python deploy file

Beyond ad-hoc commands, pyinfra defines state through operations, and the transition is deliberately gradual. First the operation runs as a command line with keyword arguments:

```sh
# Install iftop apt package if not present
pyinfra @docker/ubuntu apt.packages iftop update=true _sudo=true
```

The same operation then moves into a Python file, deploy.py, unchanged in meaning:

```py
from pyinfra.operations import apt

apt.packages(
    name="Ensure iftop is installed",
    packages=['iftop'],
    update=True,
    _sudo=True,
)
```

This is the design's quiet strength, the CLI and the API are the same operations with the same arguments, so nothing is learned twice and nothing is rewritten when a one-liner grows into a deployment. The underscore-prefixed _sudo is an operation-level global argument, documented with the rest on the arguments page, and the operations themselves, apt.packages and its siblings, are the equivalent of Ansible modules.

## inventory.py and the building blocks

Hosts live in their own file, an inventory.py whose shape is plain Python:

```py
targets = ["@docker/ubuntu", "my-test-server.net"]
```

Running a deployment is then both files together:

```sh
pyinfra inventory.py deploy.py
```

The README names the triangle explicitly, inventory, operations and Python code, and states that combining them deploys anything. Because the inventory is Python, host groups, variable computation and environment-specific logic are ordinary code rather than inventory DSL features, and the docs carry dedicated pages for inventory and data, global arguments and the CLI itself. The examples repository, pyinfra-examples, collects complete deployments, and the getting started and using operations guides fill the gap between the quickstart and production use, a documentation map that mirrors the tool's three concepts rather than fighting them.

## Idempotency, diffs and dry runs

The operations are idempotent, running one twice produces the change once, and that property enables the two safety features that decide whether a tool gets used on production. Diffs show exactly what an operation would change before it changes anything, the same review affordance that made configuration management reviewable. Dry runs execute the whole deployment in a no-op mode, so a Tuesday afternoon deploy can be rehearsed against the real inventory on Tuesday morning. And when something misbehaves, the -vvv flag streams realtime stdin, stdout and stderr from the targets, the README calling it instant debugging, which is the counterpoint to the buffered, post-hoc logs that make fleet automation painful at two in the morning. Together the three form a change-safety story stronger than speed alone, speed without rehearsal is just the ability to break things sooner.

## gevent, paramiko and the concurrency floor

The dependency list reveals the machinery behind the thousands-of-hosts claim. gevent provides coroutine-based concurrency, the green-thread model that lets one process hold many simultaneous SSH sessions without a process per host, which is where the predictable performance at scale comes from. paramiko supplies the SSH transport, pinned above 2.11 for a ProxyJump timeout fix the pyproject comments cite by issue number, the fingerprint of maintainers who debug the transport layer itself. The CLI is built on click and cyclopts, templating on Jinja2, configuration validation on pydantic with typeguard checking types, and distro handles target OS detection. Python support runs 3.10 through 3.14, and the package declares itself Production/Stable, aimed at developers and system administrators in equal measure.

## A 3.x line with an AI policy in the tree

The project's current major is the 3.x branch, the default branch itself, with releases at a steady clip, v3.9.1 and v3.9.2 on the same June 2026 day and v3.10.0 in July, and the last push on 2026-09-22. The repository carries the now-familiar assistant configuration, .agents, .claude, AGENTS.md and CLAUDE.md, and goes one step further with an AI_POLICY.md stating how artificial intelligence contributes to the project, a document most projects have not yet thought to write. A pyinfra-metadata.toml and schema file version the project's own metadata, codecov tracks coverage with a test group spanning pytest, testinfra for integration and freezegun for time-dependent behavior. Change tracking runs through a CHANGELOG, contributions have a guide, and support routes through the documented help page, the posture of a mature tool whose maintenance is organized rather than heroic.

## Conclusion

Use pyinfra when your team already thinks in Python and wants deployments, provisioning and ad-hoc execution as debuggable code with real diffing and dry runs, rather than YAML DSL wrangling. Use Ansible when its enormous module ecosystem and organizational ubiquity outweigh language preference, and Terraform when the job is declarative cloud resource lifecycle rather than configuring hosts. Verify first that your targets speak SSH or match a connector, install through uv for an isolated CLI, and start with the quickstart's exec and apt.packages commands before writing a full deploy file.

## FAQ

### Can Python be used for infrastructure automation?

Yes. pyinfra is a Python tool that turns Python code into shell commands and runs them on servers over SSH, on Docker containers, on the local machine and through other connectors. It supports ad-hoc execution and idempotent declarative operations, scaling from one host to thousands.

### How is pyinfra different from Ansible?

The README's own summary is ansible but Python instead of YAML, and a lot faster. Deployments and inventories are plain Python files, operations support diffs and dry runs, and debugging streams realtime command output at -vvv, while Ansible uses YAML playbooks with its own DSL.

### Which targets can pyinfra run against?

Any host with shell access over SSH, plus connectors for Docker containers, the local machine, Terraform, Vagrant and others documented on the connectors page. The at-sign prefix selects the connector, as in @docker/ubuntu or @local.

## Sources

- [License: MIT](https://github.com/pyinfra-dev/pyinfra/blob/3.x/LICENSE)
- [Project website](https://pyinfra.com)
- [pyinfra-dev/pyinfra on GitHub](https://github.com/pyinfra-dev/pyinfra)
- [README](https://github.com/pyinfra-dev/pyinfra/blob/3.x/README.md)
- [Releases](https://github.com/pyinfra-dev/pyinfra/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pyinfra-dev-pyinfra
