Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/pyinfra-dev/pyinfra/llms.txt

Use this file to discover all available pages before exploring further.

The local connector executes operations on the local machine using subprocesses. This is useful for managing the machine you’re running pyinfra from.

Overview

The @local connector runs commands on your local machine instead of connecting to remote hosts via SSH.
# Execute on local machine
pyinfra @local apt.packages nginx update=true _sudo=true

# Run deploy file locally
pyinfra @local deploy.py
The local connector is only compatible with macOS and Linux hosts. Windows is not supported.

Basic Usage

CLI Examples

# Install package locally
pyinfra @local apt.packages curl _sudo=true

# Run shell commands
pyinfra @local server.shell "echo 'Hello from local'"

# Execute deploy
pyinfra @local deploy.py

Inventory

# inventory.py
hosts = ["@local"]
You can only have one @local host in your inventory. Multiple @local entries will raise an InventoryError.

Example Deploy

Here’s a complete example managing the local machine:
# inventory.py
hosts = ["@local"]
# deploy.py
from pyinfra import host
from pyinfra.operations import apt, files, systemd
from pyinfra.facts.server import LinuxDistribution

# Get local system info
distro = host.get_fact(LinuxDistribution)
print(f"Configuring local {distro['name']} {distro['version']} system")

# Update system
apt.update(
    name="Update apt cache",
    _sudo=True,
)

# Install development tools
apt.packages(
    name="Install dev tools",
    packages=[
        "build-essential",
        "git",
        "curl",
        "vim",
    ],
    _sudo=True,
)

# Create project directory
files.directory(
    name="Create projects directory",
    path="~/projects",
    present=True,
)

# Configure git
files.line(
    name="Set git user.name",
    path="~/.gitconfig",
    line="[user]",
    present=True,
)

files.line(
    name="Set git user.email",
    path="~/.gitconfig",
    line="\temail = user@example.com",
    present=True,
)
# Execute locally with sudo
pyinfra @local deploy.py

Use Cases

Local Development Setup

Bootstrap a development environment:
# setup_dev.py
from pyinfra.operations import apt, pip, git, files

# Install system packages
apt.packages(
    name="Install system dependencies",
    packages=[
        "python3",
        "python3-pip",
        "python3-venv",
        "postgresql",
        "redis-server",
    ],
    _sudo=True,
)

# Clone project
git.repo(
    name="Clone project repository",
    src="https://github.com/user/project.git",
    dest="~/projects/project",
)

# Create virtualenv
pip.virtualenv(
    name="Create Python virtualenv",
    path="~/projects/project/venv",
)

# Install Python dependencies
pip.packages(
    name="Install Python packages",
    packages=["requirements.txt"],
    virtualenv="~/projects/project/venv",
)

CI/CD Pipelines

Use in CI/CD to configure the build agent:
# ci_setup.py
from pyinfra.operations import apt, files

# Install build tools
apt.packages(
    name="Install CI tools",
    packages=[
        "docker.io",
        "ansible",
        "terraform",
    ],
    _sudo=True,
)

# Create workspace
files.directory(
    name="Create CI workspace",
    path="/workspace",
    present=True,
    _sudo=True,
)

Testing Operations

Test operations locally before deploying remotely:
# test_deploy.py
from pyinfra.operations import server

# Test command locally
server.shell(
    name="Test command",
    commands=["ls -la /tmp"],
)
# Test locally first
pyinfra @local test_deploy.py

# Then deploy to production
pyinfra inventory.py test_deploy.py

Combining Local and Remote Hosts

You can mix local and remote hosts in the same inventory:
# inventory.py
hosts = [
    "@local",
    "web1.example.com",
    "web2.example.com",
]
Use host data to differentiate:
# deploy.py
from pyinfra import host
from pyinfra.operations import files

if host.name == "@local":
    # Local-specific operations
    files.directory(
        name="Create local logs directory",
        path="~/logs",
    )
else:
    # Remote-specific operations
    files.directory(
        name="Create remote logs directory",
        path="/var/log/app",
        _sudo=True,
    )

Privilege Escalation

Use sudo for operations requiring elevated privileges:
# Install system package
apt.packages(
    name="Install nginx",
    packages=["nginx"],
    _sudo=True,  # Uses sudo
)

# Create file in home directory (no sudo needed)
files.file(
    name="Create user file",
    path="~/test.txt",
    content="Hello",
)

API Usage

Use the local connector in API mode:
from pyinfra import Config, Inventory, State
from pyinfra.api import add_op
from pyinfra.operations import server

# Create inventory with local host
inventory = Inventory(
    (["@local"], {})
)

config = Config(SUDO=True)
state = State(inventory, config)
state.init(inventory, config)

# Connect
for host in inventory:
    host.connect()

# Add operations
add_op(
    state,
    server.shell,
    commands=["uname -a"],
)

# Execute
from pyinfra.api.operations import run_ops
run_ops(state)

Command Execution

The local connector uses subprocess to execute commands:
# This command
server.shell(commands=["ls /tmp"])

# Becomes
import subprocess
subprocess.run(["sh", "-c", "ls /tmp"])

# With sudo
server.shell(commands=["ls /tmp"], _sudo=True)

# Becomes
subprocess.run(["sudo", "-H", "-n", "sh", "-c", "ls /tmp"])

Environment Variables

Pass environment variables to local commands:
from pyinfra import Config
from pyinfra.operations import server

config = Config(
    ENV={
        "PATH": "/custom/bin:$PATH",
        "MY_VAR": "value",
    }
)

server.shell(
    name="Command with env",
    commands=["echo $MY_VAR"],
)

Timeout

Set command timeouts:
from pyinfra.operations import server

server.shell(
    name="Long running command",
    commands=["./long_script.sh"],
    _timeout=300,  # 5 minute timeout
)

Limitations

The local connector has some limitations:
  • Only one @local host per inventory
  • Not compatible with Windows
  • No connection pooling (each command is a new subprocess)
  • File operations copy files unnecessarily (source and dest are same filesystem)

Security Considerations

When using @local, be careful with:
  • File permissions (operations run as current user)
  • Sudo access (may require password prompts)
  • Destructive operations (directly affect your machine)
  • User input (risk of command injection)
Always test with --dry flag first:
pyinfra @local deploy.py --dry

Comparison with Other Connectors

Feature@local@ssh@docker
Remote executionNoYesYes
Requires SSHNoYesNo
Requires DockerNoNoYes
Platform supportLinux/macOSAnyLinux
Use caseLocal managementRemote serversTesting/containers

Source Reference

Location: src/pyinfra/connectors/local.py:26

Key Properties

  • handles_execution - This connector handles command execution directly

Key Methods

  • run_shell_command() - Execute command locally (line 50)
  • put_file() - Copy file locally (line 107)
  • get_file() - Copy file locally (line 132)

Build docs developers (and LLMs) love