The Docker connector allows pyinfra to target Docker containers as inventory. You can create new containers from images, execute operations, and save the results as new images.
Overview
The Docker connector operates in two modes:
- Image Mode - Creates a new container from an image, executes operations, saves to a new image, and removes the container
- Container Mode - Executes operations against an existing running container
# Create container from image
pyinfra @docker/ubuntu:22.04 ...
# Execute against running container
pyinfra @docker/container_id ...
# Multiple containers in parallel
pyinfra @docker/ubuntu:22.04,@docker/alpine:3.18 ...
The Docker connector is great for testing pyinfra operations locally without needing remote SSH hosts.
Connector Data
Docker image name or existing container ID.
Platform to use for Docker image (e.g., linux/amd64, linux/arm64).
Architecture to use for Docker image (e.g., amd64, arm64).
Basic Usage
From Docker Image
# Install nginx in Ubuntu container
pyinfra @docker/ubuntu:22.04 apt.packages nginx update=true _sudo=true
# Multiple operations
pyinfra @docker/alpine:3.18 deploy.py
From Existing Container
# Get container ID
docker ps
# CONTAINER ID IMAGE ...
# 2beb8c15a1b1 ubuntu ...
# Execute against running container
pyinfra @docker/2beb8c15a1b1 server.shell "echo hello"
Inventory Examples
Single Container
# inventory.py
hosts = ["@docker/ubuntu:22.04"]
Multiple Containers
# inventory.py
hosts = [
"@docker/ubuntu:22.04",
"@docker/debian:12",
"@docker/alpine:3.18",
]
Containers with Data
# inventory.py
hosts = [
("@docker/ubuntu:22.04", {
"docker_identifier": "ubuntu:22.04",
"app_name": "web",
}),
("@docker/postgres:15", {
"docker_identifier": "postgres:15",
"app_name": "db",
}),
]
# inventory.py
hosts = [
("@docker/ubuntu:22.04", {
"docker_identifier": "ubuntu:22.04",
"docker_platform": "linux/amd64",
}),
("@docker/ubuntu:22.04", {
"docker_identifier": "ubuntu:22.04",
"docker_platform": "linux/arm64",
}),
]
Creating Docker Images
When using image mode, pyinfra automatically:
- Creates a new container from the image
- Executes operations
- Commits changes to a new image
- Removes the temporary container
# Operations are saved to new image
pyinfra @docker/ubuntu:22.04 \
apt.packages nginx update=true _sudo=true \
--save-image my-nginx:latest
Image Mode vs Container Mode
Image Mode
Used when specifying an image name:
# inventory.py
hosts = ["@docker/ubuntu:22.04"]
Pyinfra will:
- Create a new container:
docker run -d ubuntu:22.04 tail -f /dev/null
- Execute operations
- Optionally save to new image
- Remove container
Container Mode
Used when specifying a container ID:
# inventory.py
hosts = ["@docker/abc123def456"] # Container ID
Pyinfra will:
- Use existing container (starts it if stopped)
- Execute operations
- Leave container running
Using Podman
The Docker connector works with Podman too:
# Use podman instead of docker
from pyinfra.connectors.docker import DockerConnector
DockerConnector.docker_cmd = "podman"
hosts = ["@docker/ubuntu:22.04"]
Complete Example
Here’s a complete example creating a custom web server image:
# inventory.py
hosts = [
("@docker/ubuntu:22.04", {
"docker_identifier": "ubuntu:22.04",
}),
]
# deploy.py
from pyinfra.operations import apt, files, systemd
# Update and install packages
apt.update(
name="Update apt cache",
_sudo=True,
)
apt.packages(
name="Install nginx",
packages=["nginx"],
_sudo=True,
)
# Configure nginx
files.put(
name="Upload nginx config",
src="configs/nginx.conf",
dest="/etc/nginx/nginx.conf",
_sudo=True,
)
# Create web content
files.file(
name="Create index.html",
path="/var/www/html/index.html",
content="<h1>Hello from pyinfra!</h1>",
_sudo=True,
)
# Run and save to new image
pyinfra inventory.py deploy.py --save-image my-webserver:v1
Testing with Docker
The Docker connector is ideal for testing deployments locally:
# test_deploy.py
from pyinfra import Config, Inventory, State
from pyinfra.api import add_op
from pyinfra.operations import apt, server
# Test inventory
inventory = Inventory(
(["@docker/ubuntu:22.04"], {})
)
config = Config()
state = State(inventory, config)
state.init(inventory, config)
# Connect
for host in inventory:
host.connect()
# Test operations
add_op(state, apt.update)
add_op(state, apt.packages, packages=["curl"])
add_op(state, server.shell, commands=["curl --version"])
# Execute
from pyinfra.api.operations import run_ops
run_ops(state)
print("Test passed!")
Multiple Distributions
Test operations across multiple Linux distributions:
# inventory.py
hosts = [
# Debian-based
"@docker/ubuntu:22.04",
"@docker/ubuntu:20.04",
"@docker/debian:12",
"@docker/debian:11",
# Red Hat-based
"@docker/fedora:39",
"@docker/centos:stream9",
# Alpine
"@docker/alpine:3.18",
]
# deploy.py
from pyinfra import host
from pyinfra.facts.server import LinuxDistribution
from pyinfra.operations import apt, apk, yum
distro = host.get_fact(LinuxDistribution)
if distro["name"] in ["Ubuntu", "Debian"]:
apt.packages(
name="Install curl",
packages=["curl"],
_sudo=True,
)
elif distro["name"] == "Alpine":
apk.packages(
name="Install curl",
packages=["curl"],
)
else:
yum.packages(
name="Install curl",
packages=["curl"],
_sudo=True,
)
Limitations
The Docker connector has some limitations:
- No systemd support in containers by default
- Some privileged operations may fail
- Container must have a shell available
- File permissions may differ from regular hosts
Troubleshooting
Container Not Found
# Pull image first
docker pull ubuntu:22.04
# Then run pyinfra
pyinfra @docker/ubuntu:22.04 ...
Permission Denied
Ensure Docker daemon is accessible:
# Add user to docker group
sudo usermod -aG docker $USER
# Or use sudo
sudo pyinfra @docker/ubuntu:22.04 ...
Specify platform explicitly:
hosts = [
("@docker/ubuntu:22.04", {
"docker_platform": "linux/amd64",
}),
]
- docker connector - Executes commands in local Docker containers
- dockerssh connector - SSHs into Docker containers running on remote hosts
For remote Docker containers, use the dockerssh connector.
Source Reference
Location: src/pyinfra/connectors/docker.py:40
Key Properties
docker_cmd - Command to use (default: “docker”, can be “podman”)
handles_execution - This connector handles command execution
container_id - ID of running container
Key Methods
connect() - Create/connect to container
disconnect() - Stop/cleanup container
run_shell_command() - Execute command in container
put_file() - Copy file into container
get_file() - Copy file from container