Skip to content

spboyer/pycontainer-build

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

42 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🐍 pycontainer-build

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.


🎯 Why This Exists

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 build

That'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.


πŸš€ Quick Start

Installation

# 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

Build Your First Image

# 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 --verbose

Output

Creates 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

Testing Locally

Important: pycontainer creates OCI-compliant image layouts, not Docker-specific images. To test locally:

Option 1: Use Skopeo (Recommended)

# 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

Option 2: Use Podman (Native OCI Support)

# Install Podman
brew install podman  # macOS

# Run directly from OCI layout
podman run --rm -p 8000:8000 oci:dist/image:myapp

Option 3: Push to Registry (Production)

# 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:latest

See Local Development Guide for detailed testing instructions.


✨ Features

Current Capabilities (Phases 0-2, 4 βœ…)

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.toml scripts, falls back to python -m
  • βœ… Creates OCI-compliant images β€” Complete OCI image layout v1
  • βœ… Command-line interface β€” Simple pycontainer build workflow
  • βœ… 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-python in 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

Coming Soon

  • πŸ”œ Toolchain integrations β€” Poetry, Hatch, Azure Developer CLI (Phase 3)
  • πŸ”œ Full multi-arch builds β€” Actual cross-compilation for ARM64, AMD64 (Phase 4+)

πŸ“– How It Works

Architecture Overview

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)

Build Process

  1. Project Discovery β€” Reads pyproject.toml, detects entry points and structure
  2. File Collection β€” Gathers source files based on auto-detected or configured paths
  3. Layer Creation β€” Packs files into tar archive with correct /app/ prefixes
  4. OCI Generation β€” Creates manifest and config JSON per OCI Image Spec v1
  5. Output β€” Writes image layout to disk (registry push coming in Phase 1)

🧩 Programmatic Usage

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

πŸ”Œ Integrations

pycontainer-build integrates seamlessly with popular Python tools:

Poetry Plugin

poetry self add poetry-pycontainer
poetry build-container --tag myapp:latest --push

See full documentation β†’

Hatch Plugin

pip install hatch-pycontainer
hatch build  # Builds both wheel and container

See full documentation β†’

GitHub Actions

jobs:
  build:
    uses: spboyer/pycontainer-build/.github/workflows/pycontainer-build.yml@main
    with:
      tag: ghcr.io/${{ github.repository }}:latest
      push: true

See full documentation β†’

Azure Developer CLI

# azure.yaml
hooks:
  build:
    run: pycontainer build --tag ${SERVICE_IMAGE_NAME} --push

See full documentation β†’

VS Code Extension

Install from VS Code Marketplace or command palette:

  • "Build Container Image"
  • "Build and Push Container Image"

See full documentation β†’


πŸŽ“ Configuration

Auto-Detection (Zero Config)

By default, pycontainer auto-detects:

  • Base image: Python version from requires-python in pyproject.toml (e.g., >=3.11 β†’ python:3.11-slim)
  • Entry point: First [project.scripts] entry in pyproject.toml
  • Include paths: src/, app/, or <package>/ dirs + pyproject.toml, requirements.txt
  • Working directory: /app/
  • Architecture: amd64/linux

Explicit Configuration

# 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 \
  --push

Base Image & Dependencies:

  • --base-image IMAGE β€” Base image to build on (auto-detected from requires-python if 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 from pycontainer.toml
  • --sbom FORMAT β€” Generate SBOM (spdx or cyclonedx)
  • --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)

Python API

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()

Configuration File (pycontainer.toml)

[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"

πŸ—ΊοΈ Roadmap

βœ… Phase 0: Foundation (COMPLETE)

  • Core OCI image generation
  • Basic CLI and Python API
  • Project introspection and auto-detection
  • File packing and layer creation

βœ… Phase 1: Registry & Caching (COMPLETE)

  • 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

βœ… Phase 2: Base Images & Dependencies (COMPLETE)

  • 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)

βœ… Phase 3: Toolchain Integrations (COMPLETE)

  • Poetry plugin (poetry build-container)
  • Hatch build hook
  • Azure Developer CLI (azd) integration
  • GitHub Actions reusable workflow
  • VS Code extension

βœ… Phase 4: Polish & Production Readiness (COMPLETE)

  • Framework auto-detection (FastAPI, Flask, Django)
  • pycontainer.toml configuration schema
  • SBOM (Software Bill of Materials) generation
  • Reproducible builds (deterministic layer creation)
  • Platform configuration (metadata for multi-arch)
  • Verbose logging and diagnostics

🎯 Design Goals

1. Native Python Experience

Container building should feel like a native Python operation, not a Docker side quest.

2. Zero External Dependencies

No Docker daemon, CLI tools, or system packages required. Pure Python stdlib + OCI specs.

3. Language-Integrated

Understand Python projects natively β€” entry points, modules, dependencies, project structure.

4. AI-Friendly API

Simple, programmable interface for agentic workflows and Copilot-generated scaffolding.

5. Cross-Platform & Daemonless

Works in GitHub Codespaces, Dev Box, locked-down environments β€” anywhere Python runs.


🀝 Why This Matters

For Python Developers

  • Simpler workflow than Dockerfiles
  • No Docker Desktop licensing concerns
  • Faster onboarding for containerization

For Microsoft & Azure

  • 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

For the Python Ecosystem

  • 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

πŸ”¬ Current Limitations

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.


πŸ› οΈ Development

Prerequisites

  • Python 3.10+ (uses tomllib for TOML parsing)
  • No other dependencies β€” pure stdlib

Install for Development

git clone https://github.com/microsoft/pycontainer-build.git
cd pycontainer-build
pip install -e .

Test a Build

# 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:test

Code Style

This 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.


πŸ“š Resources

Documentation

External References


πŸ“„ License

MIT License - See LICENSE for details


πŸ™ Acknowledgments

Inspired by:


Built with ❀️ by the Microsoft Python & Azure tooling teams

About

Docker-free OCI image builder for Python projects

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •