Generating Dockerfiles
appligator.airflow.gen_dockerfile.generate assembles a Docker build
context and writes a Dockerfile for running workflow steps inside a
container orchestrator (e.g. Airflow's KubernetesPodOperator).
It does not invoke docker build — you control when and how the image
is built, which makes it easy to integrate into any CI pipeline.
How it works
The function produces a two-stage image:
| Stage | Base image | Purpose |
|---|---|---|
| build | ghcr.io/prefix-dev/pixi:<version>-bookworm-slim |
Resolves and installs the pixi environment |
| runtime | debian:bookworm-slim |
Ships only the installed environment, not the source tree |
The pixi environment is copied from the build stage into the lean runtime image, keeping the final image small and reproducible.
Minimal example
Create a build_docker_image.py at the root of your pixi workspace (where
pyproject.toml and pixi.lock live) and run it with pixi run python
build_docker_image.py:
from pathlib import Path
from appligator.airflow.gen_dockerfile import generate
generate(
output_dir=Path("image"),
)
This writes image/Dockerfile and copies the necessary workspace files into
image/. Build the image manually:
docker build -t myrepo/myimage:latest image/
Non-editable local packages
In a monorepo where local packages are declared as pixi path-dependencies,
pixi install installs them in editable mode — meaning the runtime image
would need to carry the entire source tree. Use packages_dir and
local_packages to convert them to non-editable installs instead, so only
the compiled environment is shipped:
generate(
output_dir=Path("image"),
packages_dir=Path("packages"), # directory with local packages
local_packages=["my-utils", "my-app"], # names inside packages_dir
)
generate will:
- Copy only the listed packages into the build context.
- Run
pixi install --lockedto resolve the full environment. - Run
pip install --no-deps --no-build-isolationon each listed package to overwrite the editable install with a non-editable one. - Not copy
packages/into the runtime stage — the source tree is no longer needed.
The pip binary path is derived from workdir and pixi_env automatically;
you never need to hardcode it.
Adding extra packages at build time
Some packages are only needed inside the container (e.g. Airflow providers)
and should not pollute the local development environment. Pass them via
build_commands, which runs before pixi install:
generate(
output_dir=Path("image"),
build_commands=["RUN pixi add apache-airflow-providers-cncf-kubernetes"],
)
Application-specific runtime steps
Use runtime_commands for any COPY, RUN, or ENV instructions that
belong to your application rather than the generic image:
generate(
output_dir=Path("image"),
extra_files={"sensor_data": Path("data/sensor_data")},
runtime_commands=[
"COPY ./sensor_data /opt/pixi/sensor_data",
"RUN myapp-cli prefetch-data",
'RUN python -c "import myapp; print(\'myapp OK\')"',
],
)
extra_files copies host files into the build context so Docker can COPY
them. The corresponding COPY instruction goes in runtime_commands.
Full parameter reference
| Parameter | Type | Default | Description |
|---|---|---|---|
package_manager |
str |
"pixi" |
Package manager to use. Only "pixi" is currently supported. |
workdir |
str |
"/opt/pixi" |
WORKDIR in both stages. All env files and the entry point live here. |
output_dir |
Path |
Path("image") |
Directory where the build context is assembled. Recreated on every call. |
packages_dir |
Path \| None |
None |
Directory of local path-dependencies to copy into the build context. |
extra_files |
dict[str, Path] \| None |
None |
Extra files/dirs to add to the build context. Keys are names inside output_dir. |
build_commands |
list[str] \| None |
None |
Raw Dockerfile instructions inserted in the build stage before pixi install. |
local_packages |
list[str] \| None |
None |
Package names inside packages_dir to install as non-editable. Requires packages_dir. |
post_install_commands |
list[str] \| None |
None |
Raw Dockerfile instructions inserted in the build stage after pixi install. |
runtime_commands |
list[str] \| None |
None |
Raw Dockerfile instructions appended to the runtime stage before the ENV block. |
runtime_apt_packages |
list[str] \| None |
None |
Extra Debian packages to install at runtime (in addition to libgomp1). |
pixi_version |
str |
"0.50.2" |
Version tag of the ghcr.io/prefix-dev/pixi build image. |
pixi_env |
str |
"default" |
Name of the pixi environment to install and ship. |
Out-of-context path dependencies
If your pyproject.toml contains path-dependencies that point outside the
build context (e.g. { path = "../eozilla/appligator", editable = true } for
local development), generate strips them automatically before copying
pyproject.toml into the build context. Dependencies pointing inside the
context (e.g. packages/my-pkg) are kept as-is.
This means you can safely use a local-deps pixi feature for development
without it breaking the Docker build.
Entry point
Every generated image includes run_step.py (from appligator.airflow) as
its entry point. It is invoked by the Airflow KubernetesPodOperator with a
JSON payload specifying the function to run and its inputs:
ENTRYPOINT ["python", "/opt/pixi/run_step.py"]