Build OCI container images from Python projects β no Docker required
A native, Docker-free container image builder for Python, inspired by .NET's PublishContainer. Create production-ready OCI images using pure Python, without Dockerfiles or Docker daemon.
Today, containerizing Python applications requires:
- Writing and maintaining Dockerfiles
- Installing Docker Desktop or Docker Engine
- Understanding Docker-specific concepts and commands
- Managing multi-stage builds for dependencies
pycontainer-build provides a simpler path:
pycontainer buildThat's it. No Dockerfile. No Docker daemon. Just pure Python creating OCI-compliant container images.
This mirrors the elegant developer experience that .NET provides with its SDK's native container publishing β now available for Python.
# Using pip
pip install -e .
# Using uv (faster)
uv pip install -e .
# Or run directly with uvx (no install needed)
uvx --from git+https://github.com/spboyer/pycontainer-build pycontainer build --tag myapp:latest# Simple build (auto-detects Python version and base image)
pycontainer build --tag myapp:latest
# Build with custom base image and dependencies
pycontainer build \
--tag myapp:v1 \
--base-image python:3.12-slim \
--include-deps
# Build FastAPI app (auto-detected, entrypoint configured)
pycontainer build --tag api:latest --context ./my-fastapi-app
# Build with SBOM for security compliance
pycontainer build \
--tag myapp:v1 \
--sbom spdx \
--config pycontainer.toml
# Build and push to registry
pycontainer build --tag ghcr.io/user/myapp:v1 --push
# Build for different platform (e.g., amd64 from ARM Mac)
pycontainer build --tag myapp:latest --platform linux/amd64
# Dry-run to preview (verbose mode)
pycontainer build --tag test:latest --dry-run --verboseCreates a complete OCI image layout at dist/image/:
dist/image/
βββ index.json # OCI index (manifest list)
βββ oci-layout # Version marker
βββ blobs/sha256/
β βββ <manifest-digest> # Manifest blob
β βββ <config-digest> # Config blob
β βββ <layer-digest> # Application layer (tar)
βββ refs/tags/
βββ <tag-name> # Tag reference
Important: pycontainer creates OCI-compliant image layouts, not Docker-specific images. To test locally:
# Install skopeo
brew install skopeo # macOS
# OR: sudo apt-get install skopeo (Ubuntu)
# Copy OCI layout to Docker
skopeo copy oci:dist/image docker-daemon:myapp:latest
docker run -p 8000:8000 myapp:latest# Install Podman
brew install podman # macOS
# Run directly from OCI layout
podman run --rm -p 8000:8000 oci:dist/image:myapp# Build and push (works with any container runtime)
pycontainer build --tag ghcr.io/user/myapp:latest --push
# Pull and run with Docker or Podman
docker pull ghcr.io/user/myapp:latest
docker run -p 8000:8000 ghcr.io/user/myapp:latestSee Local Development Guide for detailed testing instructions.
Foundation & Registry (Phases 0-1):
- β Zero Docker dependencies β Pure Python implementation
- β
Auto-detects Python project structure β Finds
src/,app/, entry points - β
Infers entrypoints β Reads
pyproject.tomlscripts, falls back topython -m - β Creates OCI-compliant images β Complete OCI image layout v1
- β
Command-line interface β Simple
pycontainer buildworkflow - β Programmatic API β Use as a library in your tools
- β Registry push support β Push to GHCR, ACR, Docker Hub via Registry v2 API
- β Blob existence checks β Skip uploading layers that already exist
- β Progress reporting β Visual feedback during push operations
- β Multi-provider authentication β GitHub tokens, Docker config, Azure CLI, env vars
- β OAuth2 token exchange β Automatic bearer token flow with Www-Authenticate
- β Credential auto-discovery β Tries multiple auth sources automatically
- β Layer caching β Content-addressable storage with LRU eviction
- β Cache invalidation β Detects file changes via mtime + size checks
- β Fast incremental builds β Reuses unchanged layers from cache
Base Images & Dependencies (Phase 2):
- β
Smart base image detection β Auto-selects Python base image from
requires-pythonin pyproject.toml - β
Base image support β Build on top of
python:3.11-slim,python:3.12-slim, distroless, etc. - β Layer merging β Combines base image layers with application layers
- β Config inheritance β Merges env vars, labels, working dir from base images
- β Dependency packaging β Include pip packages from venv or requirements.txt
- β Distroless detection β Auto-handles shell-less base images
Production Features (Phase 4):
- β Framework auto-detection β FastAPI, Flask, Django automatically configured
- β
Configuration files β Load settings from
pycontainer.toml - β SBOM generation β Create SPDX 2.3 or CycloneDX 1.4 security manifests
- β Reproducible builds β Deterministic layer creation with fixed timestamps
- β Cross-platform builds β Build linux/amd64 from ARM, linux/arm64 from x86, etc.
- β Platform auto-selection β Pulls correct architecture variant from multi-arch base images
- β
Verbose logging β Detailed build progress with
--verbose - β
Dry-run mode β Preview builds with
--dry-run
- π Toolchain integrations β Poetry, Hatch, Azure Developer CLI (Phase 3)
- π Full multi-arch builds β Actual cross-compilation for ARM64, AMD64 (Phase 4+)
cli.py (entry point)
ββ> builder.py (orchestrates build)
ββ> config.py (build configuration)
ββ> project.py (Python project introspection)
ββ> oci.py (OCI spec structs)
ββ> fs_utils.py (file system helpers)
- Project Discovery β Reads
pyproject.toml, detects entry points and structure - File Collection β Gathers source files based on auto-detected or configured paths
- Layer Creation β Packs files into tar archive with correct
/app/prefixes - OCI Generation β Creates manifest and config JSON per OCI Image Spec v1
- Output β Writes image layout to disk (registry push coming in Phase 1)
Use as a library in your Python tools:
from pycontainer.config import BuildConfig
from pycontainer.builder import ImageBuilder
config = BuildConfig(
tag="myapp:latest",
context_path="/path/to/app",
env={"ENV": "production"},
include_paths=["src/", "pyproject.toml"]
)
builder = ImageBuilder(config)
builder.build() # Creates dist/image/Perfect for integration with:
- Azure Developer CLI (azd) β Custom build strategies (docs)
- GitHub Actions β Automated CI/CD workflows (docs)
- Poetry/Hatch β Build plugins (plugins)
- VS Code β Extension for container builds (plugin)
- AI agents β Copilot, MCP servers, automated scaffolding
pycontainer-build integrates seamlessly with popular Python tools:
poetry self add poetry-pycontainer
poetry build-container --tag myapp:latest --pushpip install hatch-pycontainer
hatch build # Builds both wheel and containerjobs:
build:
uses: spboyer/pycontainer-build/.github/workflows/pycontainer-build.yml@main
with:
tag: ghcr.io/${{ github.repository }}:latest
push: true# azure.yaml
hooks:
build:
run: pycontainer build --tag ${SERVICE_IMAGE_NAME} --pushInstall from VS Code Marketplace or command palette:
- "Build Container Image"
- "Build and Push Container Image"
By default, pycontainer auto-detects:
- Base image: Python version from
requires-pythoninpyproject.toml(e.g.,>=3.11βpython:3.11-slim) - Entry point: First
[project.scripts]entry inpyproject.toml - Include paths:
src/,app/, or<package>/dirs +pyproject.toml,requirements.txt - Working directory:
/app/ - Architecture:
amd64/linux
# Full configuration with all options
pycontainer build \
--tag myapp:v1.2.3 \
--context /my/project \
--base-image python:3.11-slim \
--include-deps \
--workdir /app \
--env KEY=value \
--env ANOTHER=value \
--platform linux/amd64 \
--sbom cyclonedx \
--config pycontainer.toml \
--verbose \
--push \
--no-cache
# Build for ARM64 (e.g., for AWS Graviton, Apple Silicon containers)
pycontainer build \
--tag myapp:arm64 \
--platform linux/arm64 \
--pushBase Image & Dependencies:
--base-image IMAGEβ Base image to build on (auto-detected fromrequires-pythonif not specified, e.g.,python:3.11-slim)--include-depsβ Package dependencies from venv or requirements.txt
Caching Options:
--no-cacheβ Disable layer caching, force full rebuild--cache-dir PATHβ Custom cache directory (default:~/.pycontainer/cache)
Production Features:
--config FILEβ Load settings frompycontainer.toml--sbom FORMATβ Generate SBOM (spdxorcyclonedx)--platform PLATFORMβ Target platform (e.g.,linux/arm64)--verbose/-vβ Detailed build progress--dry-runβ Preview build without creating artifacts--no-reproducibleβ Disable deterministic builds
The cache automatically:
- Reuses unchanged layers across builds (content-addressable by SHA256)
- Invalidates on file content changes (mtime + size checks)
- Evicts old entries using LRU when size limit reached (default: 5GB)
from pycontainer.config import BuildConfig
from pycontainer.builder import ImageBuilder
config = BuildConfig(
tag="myapp:latest",
context_path=".",
base_image="python:3.11-slim", # Optional: auto-detected if omitted
include_deps=True,
workdir="/app",
env={"DEBUG": "false", "ENV": "production"},
labels={"version": "1.0", "maintainer": "team@example.com"},
include_paths=["src/", "lib/", "pyproject.toml"],
entrypoint=["python", "-m", "myapp"],
generate_sbom="spdx",
reproducible=True,
verbose=True
)
builder = ImageBuilder(config)
builder.build()[build]
base_image = "python:3.11-slim"
workdir = "/app"
include_deps = true
reproducible = true
[build.labels]
maintainer = "team@example.com"
version = "1.0.0"
[build.env]
PORT = "8080"
ENV = "production"
DEBUG = "false"
[registry]
url = "ghcr.io/myorg/myapp"- Core OCI image generation
- Basic CLI and Python API
- Project introspection and auto-detection
- File packing and layer creation
- Implement complete OCI image layout (index.json, refs/)
- Push images to registries via Docker Registry v2 API
- Support authentication (GHCR, ACR, Docker Hub, private registries)
- Add layer caching and reuse logic
- Digest verification and content-addressable storage
- Pull and parse base image manifests
- Layer Python app files on top of base images
- Support slim, distroless, and custom base images
- Package pip-installed dependencies into layers
- Respect base image configuration (env, labels, user)
- Poetry plugin (
poetry build-container) - Hatch build hook
- Azure Developer CLI (azd) integration
- GitHub Actions reusable workflow
- VS Code extension
- Framework auto-detection (FastAPI, Flask, Django)
-
pycontainer.tomlconfiguration schema - SBOM (Software Bill of Materials) generation
- Reproducible builds (deterministic layer creation)
- Platform configuration (metadata for multi-arch)
- Verbose logging and diagnostics
Container building should feel like a native Python operation, not a Docker side quest.
No Docker daemon, CLI tools, or system packages required. Pure Python stdlib + OCI specs.
Understand Python projects natively β entry points, modules, dependencies, project structure.
Simple, programmable interface for agentic workflows and Copilot-generated scaffolding.
Works in GitHub Codespaces, Dev Box, locked-down environments β anywhere Python runs.
- Simpler workflow than Dockerfiles
- No Docker Desktop licensing concerns
- Faster onboarding for containerization
- Unified multi-language container story (aligns with .NET, Java/Jib)
- Enables Dockerless Azure Developer CLI workflows
- First-class integration with Copilot and agentic systems
- Better dev experience in Codespaces and cloud dev environments
- A modern, standards-based approach to container builds
- Foundation for Poetry, Hatch, and other tool integrations
- Opens new possibilities for Python in cloud-native environments
Known limitations and future enhancements:
- Framework detection β Supports FastAPI, Flask, Django only (easy to extend)
- SBOM scope β Python packages only; doesn't parse OS packages from base images
Note: Cross-platform builds pull the correct architecture variant from multi-platform base images and generate proper OCI metadata. For Python applications (interpreted language), no actual cross-compilation is needed.
- Python 3.10+ (uses
tomllibfor TOML parsing) - No other dependencies β pure stdlib
git clone https://github.com/microsoft/pycontainer-build.git
cd pycontainer-build
pip install -e .# Create a test project
mkdir test_app && cd test_app
echo 'print("Hello from container!")' > app.py
cat > pyproject.toml << EOF
[project]
name = "test-app"
version = "0.1.0"
EOF
# Build it
pycontainer build --tag test:latest --verbose
# Test with skopeo + Docker
skopeo copy oci:dist/image docker-daemon:test:latest
docker run test:latest
# Or test with Podman (native OCI support)
podman run oci:dist/image:testThis codebase uses ultra-minimalist, compact Python:
- Semicolons for simple multi-statement lines
- No docstrings on trivial functions
- Aggressive use of pathlib and comprehensions
- Dataclasses over dicts for structured data
This style is intentional for the experimental phase.
- Local Development Guide β Complete guide for using pycontainer-build locally
- Azure Developer CLI Integration β Deploy to Azure with azd
- GitHub Actions Guide β Automate builds in CI/CD
- OCI Image Spec: opencontainers/image-spec
- Docker Registry v2 API: Docker Registry HTTP API V2
- .NET Native Containers: Announcing built-in container support
- Project Tracking: See
IMPLEMENTATION_PLAN.mdfor detailed roadmap
MIT License - See LICENSE for details
Inspired by:
- .NET SDK's native container support
- Jib (Java) β Daemonless container builds
- ko (Go) β Simple container images for Go
Built with β€οΈ by the Microsoft Python & Azure tooling teams