{"id":7571,"library":"pyinfra","title":"pyinfra","description":"pyinfra is an infrastructure automation tool that transforms Python code into shell commands, executing them on remote servers, local machines, or Docker containers. It offers fast, agentless deployments and scales from single servers to thousands, aiming to be a Python-based alternative to YAML-centric tools like Ansible. It is currently at version 3.7 and maintains an active development and release cadence.","status":"active","version":"3.7","language":"en","source_language":"en","source_url":"https://github.com/pyinfra-dev/pyinfra","tags":["infrastructure-as-code","devops","automation","configuration-management","provisioning","deployment"],"install":[{"cmd":"pip install pyinfra","lang":"bash","label":"Install with pip"},{"cmd":"uv tool install pyinfra","lang":"bash","label":"Install with uv (recommended)"}],"dependencies":[],"imports":[{"note":"Operations like `apt.packages`, `files.file`, `server.shell` are imported from the `pyinfra.operations` module.","symbol":"apt.packages","correct":"from pyinfra.operations import apt"},{"note":"The `host` object provides context about the current target host.","symbol":"host","correct":"from pyinfra import host"},{"note":"Facts are imported from `pyinfra.facts` to gather information about target systems.","symbol":"Hostname (fact)","correct":"from pyinfra.facts.server import Hostname"},{"note":"As of pyinfra v3, the `deploy` decorator must be called as `@deploy()`.","wrong":"@deploy","symbol":"deploy (decorator)","correct":"from pyinfra.api import deploy"}],"quickstart":{"code":"import os\nfrom pyinfra.operations import apt, files\n\n# Define target hosts (using @local for a quick test)\n# For remote SSH, change to: hosts = ['my-server.net']\n# For Docker, change to: hosts = ['@docker/ubuntu:latest']\n\n# Create a dummy inventory file if running with `pyinfra inventory.py deploy.py`\n# In a real scenario, this would connect to actual hosts.\n# If running directly, pyinfra will prompt or use @local if no inventory is specified.\n\n# deploy.py\n\n# Ensure apt packages are updated and Nginx is installed\napt.update(name=\"Update apt repositories\", _sudo=True)\napt.packages(name=\"Install Nginx\", packages=['nginx'], _sudo=True)\n\n# Ensure Nginx service is running and enabled\nfiles.file(\n    name=\"Ensure Nginx config is present\",\n    path=\"/etc/nginx/sites-available/default\",\n    contents=\"\"\"\nserver {\n    listen 80 default_server;\n    listen [::]:80 default_server;\n    root /var/www/html;\n    index index.html index.htm index.nginx-debian.html;\n    server_name _;\n    location / {\n        try_files $uri $uri/ =404;\n    }\n}\n\"\"\",\n    _sudo=True\n)\n\napt.update(\n    name=\"Run apt update (idempotent)\",\n    _sudo=True\n)\n\napt.packages(\n    name=\"Install Nginx (idempotent)\",\n    packages=[\"nginx\"],\n    _sudo=True\n)\n\n# To make this runnable directly as a Python script for illustration:\n# This part is typically handled by the `pyinfra` CLI.\n# We will simulate a direct execution for simplicity, but real use is via `pyinfra <inventory> <deploy>`.\n# import sys\n# from pyinfra.api.local import Local\n# with Local(['@local']) as state:\n#     # In a real deploy.py, the operations would run directly.\n#     # For this quickstart, we just define them as above.\n#     print(\"Deploy operations defined. Run with: pyinfra @local deploy.py\")\n#     print(\"Or via SSH: pyinfra my-host.net deploy.py\")\n\n# Example of how to execute from CLI:\n# Create a file named 'deploy.py' with the above content.\n# Then run: pyinfra @local deploy.py\n# Or to a remote host: pyinfra my-server.net deploy.py --ssh-user <your_user> --ssh-password \"$PYINFRA_SSH_PASSWORD\"\n","lang":"python","description":"This quickstart demonstrates how to define a simple pyinfra deploy to install and configure Nginx on a target. Save this code as `deploy.py`. To execute it, you would typically run `pyinfra <inventory> deploy.py` from your terminal. For local testing, use `pyinfra @local deploy.py`. To target a remote server via SSH, use `pyinfra my-server.net deploy.py --ssh-user <your_user>`. The `apt.update` and `apt.packages` operations ensure idempotency, meaning they only make changes if necessary."},"warnings":[{"fix":"Update all occurrences of `_use_sudo_password` to `_sudo_password`.","message":"The `_use_sudo_password` argument has been renamed to `_sudo_password` in pyinfra v3.x.","severity":"breaking","affected_versions":"3.0.0+"},{"fix":"Change `@deploy` to `@deploy()` wherever it is used to define a deploy function.","message":"The `@deploy` decorator must now be called as a function: `@deploy()` instead of `@deploy` in pyinfra v3.x.","severity":"breaking","affected_versions":"3.0.0+"},{"fix":"For Windows management, install `pyinfra-windows` separately and use its specific facts/operations.","message":"The `winrm` connector and `windows*` operations/facts have been removed from the core `pyinfra` package in v3.x and moved to the `pyinfra-windows` plugin.","severity":"breaking","affected_versions":"3.0.0+"},{"fix":"Enable debug mode (`pyinfra --debug`) and verbose output (`pyinfra -vvv`) to trace execution, and carefully review operation conditions.","message":"Operations can fail silently or not execute as expected without clear error messages, especially if conditions (`_if`, `_when`) are not met.","severity":"gotcha","affected_versions":"All"},{"fix":"Ensure operations include logic to check the current state of the host against the desired state. pyinfra operations are generally idempotent by default, but custom `server.shell` commands or incorrect usage can bypass this. Use `--dry` flag to preview changes.","message":"An operation might run every time, even if the desired state is already met, indicating a lack of proper idempotency checks.","severity":"gotcha","affected_versions":"All"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Import operations from their respective sub-modules, such as `from pyinfra.operations import apt`, then call `apt.packages()`.","cause":"Attempting to import a specific operation directly from `pyinfra.operations` without specifying the operation module (e.g., `apt`).","error":"ModuleNotFoundError: No module named 'pyinfra.operations.apt'"},{"fix":"Verify your SSH configuration (`~/.ssh/config`), ensure your SSH agent is running and has the correct keys, or provide explicit `--ssh-user` and `--ssh-password` (or `--ssh-password-prompt`) arguments to the `pyinfra` CLI.","cause":"Incorrect SSH credentials, missing SSH keys, or issues with SSH agent forwarding when connecting to a remote host.","error":"pyinfra.exceptions.ConnectError: SSH connection failed: Permission denied (publickey,password)."},{"fix":"Run pyinfra with `--debug` and `--log-level debug` flags. Check the conditions on the operation. If it's a `server.shell` operation, inspect the `stdout` and `stderr` after execution for clues.","cause":"The operation's `_if` or `_when` conditions were not met, causing it to be skipped, or an underlying command failed silently.","error":"Operation doesn't execute or fails without error messages in pyinfra output."},{"fix":"Debug facts by inspecting `host.get_fact(FactName)` results directly. For operations, use the `--dry` flag to see what commands *would* be run, and review the operation's source or implement explicit state checks.","cause":"The facts gathered by pyinfra do not accurately reflect the host's state, or the operation's logic for detecting changes is flawed.","error":"pyinfra reports 'no changes' but the desired state is not met, or 'changes' when no changes should occur."}]}