Skip to main content

Generic Podman container runtime

Project description

podman-runner · Simple, safe Podman containers for Python tests and scripts

PyPI Python versions

podman-runner brings the convenience of testcontainers to the rootless, daemonless world of Podman.

Start containers, wait for them to be ready, run commands, mount init scripts, map ports — all with a clean context-manager API and automatic cleanup.

Perfect for integration tests, local development scripts, or anything that needs a real database/service without Docker.

from podman_runner import Container, ContainerConfig

with Container(
    ContainerConfig(
        name="my-postgres",
        image="docker.io/library/postgres:16-alpine",
        env={"POSTGRES_PASSWORD": "secret"},
        health_cmd=["pg_isready", "-U", "postgres"],
    )
) as pg:
    pg.exec(["psql", "-U", "postgres", "-c", "CREATE TABLE test(id serial);"])
    # → container stops and is removed automatically

Features

  • Context manager → guaranteed cleanup (even on exceptions or Ctrl-C)
  • Automatic health-check polling (health_cmd)
  • Smart port mapping (fixed or dynamic)
  • Auto-mount init scripts into /docker-entrypoint-initdb.d-style directories
  • Full volume and environment support
  • Detailed pre-flight checks that catch the most common Podman pitfalls (WSL shm, Docker conflict, socket not running, Snap sandbox, etc.)
  • 100% type-annotated, fully tested, zero external runtime dependencies

Installation

Requires Podman ≥ 4.0 and Python ≥ 3.11.

pip install podman-runner

Or with the fast uv tool (recommended):

uv pip install podman-runner

Install Podman

# Ubuntu/Debian
sudo apt install podman

# Fedora
sudo dnf install podman

# macOS (via Homebrew)
brew install podman
podman machine init
podman machine start

# Windows (WSL2 recommended)
# See https://podman.io/getting-started/install.html

Quick Examples

Basic container

from podman_runner import Container, ContainerConfig

with Container(ContainerConfig(
    name="demo",
    image="docker.io/library/alpine:latest",
    command=["sleep", "infinity"],
)) as c:
    print(c.exec(["echo", "Hello Podman!"]).stdout)

PostgreSQL with init scripts

from pathlib import Path
from podman_runner import Container, ContainerConfig

script = Path("01-setup.sql")
script.write_text("CREATE TABLE users(id serial PRIMARY KEY);\n")

with Container(ContainerConfig(
    name="pg-init",
    image="docker.io/library/postgres:16-alpine",
    env={"POSTGRES_PASSWORD": "secret"},
    init_dir="/docker-entrypoint-initdb.d",
    init_scripts=[script],
    health_cmd=["pg_isready", "-U", "postgres"],
)) as pg:
    result = pg.exec(["psql", "-U", "postgres", "-c", "\\dt"])
    print(result.stdout)

Redis with health check

with Container(ContainerConfig(
    name="redis",
    image="docker.io/library/redis:alpine",
    health_cmd=["redis-cli", "ping"],
)) as r:
    assert r.exec(["redis-cli", "PING"]).stdout.strip() == "PONG"

Nginx with port mapping

import requests

with Container(ContainerConfig(
    name="web",
    image="docker.io/library/nginx:alpine",
    ports={80: 8080},        # fixed host port
    # ports={443: None},     # auto-assign free host port
)) as web:
    host_port = web.get_port(80)
    resp = requests.get(f"http://localhost:{host_port}")
    assert resp.status_code == 200

See the examples/ directory for more.

API Reference

ContainerConfig

Parameter Type Description
name str Unique container name (required)
image str Image to run (required)
command list[str] | None Override container command
env dict[str, str] | None Environment variables
ports dict[int, int | None] | None {container_port: host_port_or_None}None = auto-assign
volumes dict[Path, str] | None Host path → container path (:ro suffix for read-only)
init_dir str | None Target init directory (e.g. "/docker-entrypoint-initdb.d")
init_scripts list[Path] | None Scripts auto-mounted with 00-, 01- prefix and :ro
health_cmd list[str] | None Command that exits 0 when service is ready
health_timeout int Seconds to wait for health check (default: 30)
health_interval float Seconds between health checks (default: 1.0)

Container

Method Description
container_id: str | None Podman container ID after start
start() Start container (called automatically in context manager)
stop() Stop and remove container
exec(cmd: list[str]) Run command inside container → CompletedProcess
logs(tail: int | None = None, follow=False) Get container logs
get_port(internal: int) Return host port mapped to container port (or None)
check_status() Return current status string ("running", "exited", etc.)

Development

git clone https://github.com/Francois-NT/podman-runner.git
cd podman-runner

# Install with dev dependencies
uv sync --all-extras

# Install pre-commit hooks
uv run pre-commit install

# Run tests
uv run task unittest        # fast unit tests
uv run task integration     # requires running Podman socket

Why Podman instead of Docker?

  • No daemon → works great in CI and restricted environments
  • Rootless by default
  • Full Docker CLI compatibility
  • Native on Linux, excellent macOS/Windows support via podman machine

Similar Projects

Project Docker Podman Context Manager Init Script Support Health Polling
testcontainers-python Yes No Yes No Yes
podman-compose No Yes No No No
podman-py No Yes No No No
podman-runner No Yes Yes Yes Yes

License

MIT © François Naggar-Tremblay


Happy testing with real, rootless containers!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

podman_runner-0.1.0.tar.gz (8.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

podman_runner-0.1.0-py3-none-any.whl (9.6 kB view details)

Uploaded Python 3

File details

Details for the file podman_runner-0.1.0.tar.gz.

File metadata

  • Download URL: podman_runner-0.1.0.tar.gz
  • Upload date:
  • Size: 8.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.13

File hashes

Hashes for podman_runner-0.1.0.tar.gz
Algorithm Hash digest
SHA256 87cbc1c51c87095d253fe562847c35efbfcfa71a08416ea32e41ea1d6e822259
MD5 e587213550a275ccc42e70a33d0e96e5
BLAKE2b-256 110dfb4b2db8828682602ebe61a55a798c016d1ab95c6259a79072e6bb6e4c27

See more details on using hashes here.

File details

Details for the file podman_runner-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for podman_runner-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ac12e9e9600067bf1de8b99d4c5e932e6b384826477228e5a9c725615af0a792
MD5 e187321f4b702b7eb6ef30470629e9d2
BLAKE2b-256 5b62b8c9b69a6f68d68c38792606517f67fa248d189018026fd01df8e0db9bc1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page