Containerfile Configuration
The ContainerfileConfigFile manages the project's Containerfile for building
container images with Podman or Docker.
Overview
Creates a production-ready Containerfile in the project root that:
- Uses the latest compatible Python slim image
- Installs dependencies with uv
- Runs as non-root user for security
- Sets up proper entrypoint and command
- Optimizes for layer caching
Inheritance
Inherits from: StringConfigFile
What this means:
- File is plain text (not YAML/TOML/JSON)
- Validation checks if required content is present
- Users can add custom content before or after required layers
- File is considered correct if all required layers exist in the file
File Location
Path: Containerfile (project root)
No extension: The file is named exactly Containerfile with no extension,
following Podman/Buildah conventions.
How It Works
Automatic Generation
When initialized via uv run pyrig mkroot, the Containerfile is automatically
created with all required layers:
- Base image: Uses Python slim image matching your
requires-pythonconstraint - Working directory: Sets up workspace named after your project
- UV installation: Copies uv binary from official image
- Dependency files: Copies metadata files for dependency installation
- User setup: Creates non-root
appuserfor security - Source code: Copies your package with proper ownership
- Dependencies: Installs runtime dependencies (excludes dev group)
- Cleanup: Removes unnecessary files to reduce image size
- Entrypoint: Sets up
uv run <project>as entrypoint - Default command: Runs your main module by default
Layer Structure
The Containerfile is built from discrete layers returned by get_layers():
[
"FROM python:3.12-slim",
"WORKDIR /my-project",
"COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv",
"COPY README.md LICENSE pyproject.toml uv.lock ./",
"RUN useradd -m -u 1000 appuser",
"RUN chown -R appuser:appuser .",
"USER appuser",
"COPY --chown=appuser:appuser my_project my_project",
"RUN uv sync --no-group dev",
"RUN rm README.md LICENSE pyproject.toml uv.lock",
'ENTRYPOINT ["uv", "run", "my-project"]',
'CMD ["main"]'
]
Validation Logic
Validation checks that all required layers are present. You can add comments, custom layers, or build arguments as long as all required layers exist.
Dynamic Configuration
The Containerfile adapts automatically:
- Python version: Uses the highest version allowed by
requires-python - Project names: Uses project/package names from
pyproject.toml - Entrypoint: Configured to run your project via uv
Usage
Building the Image
# With Podman (recommended)
podman build -t my-project .
Docker should work as well, but Podman is recommended for its daemonless and rootless architecture.
Running the Container
The entrypoint and default command allow running the container directly. The default command runs the main module, but you can pass custom arguments.
# Run with default command (main module)
podman run my-project
# Run with custom command
podman run my-project --help
# Run with custom subcommand
podman run my-project mysubcommand --option value
Customization
You can add custom layers while keeping pyrig's required layers:
# Custom build argument
ARG BUILD_DATE
# Required pyrig layers...
FROM python:3.12-slim
WORKDIR /myproject
# ... rest of required layers ...
# Custom copy
COPY my_custom_file.txt ./
As long as all required layers are present, validation passes.
Security Features
- Non-root user: Runs as
appuser(UID 1000) instead of root - Minimal base: Uses slim Python image to reduce attack surface and keep container size small and lightweight
- No dev dependencies: Only installs runtime dependencies
- Proper ownership: Files owned by appuser, not root
Best Practices
- Keep required layers: Don't remove pyrig's generated layers
- Add custom layers after: Append your customizations at the end
- Do not use .containerignore: Should not be necessary as we only copy the package folder and not the entire project. All copied config file are deleted in the last layer.