Skip to main content

pdm-dockerize

CI pre-commit.ci status PyPI PyPI - License codecov

Help generating docker image from PDM projects.

Installation

Install pdm-dockerize:

With uv

If you installed pdm with uv:

uv tool install pdm --with pdm-dockerize

With pipx

If you installed pdm with pipx and want to have the command for all projects:

pipx inject pdm pdm-dockerize

With pip

If you manually installed pdm with pip, just install the extra dependency in the same environment:

pip install pdm-dockerize

With pdm

You can also install it as a standard pdm plugin.

Either globally:

pdm self add pdm-dockerize

Either as a local plugin in your project:

[tool.pdm]
plugins = [
    "pdm-dockerize",
]

Then:

pdm install --plugins

Usage

Just use pdm dockerize in your multistage build:

# syntax=docker/dockerfile:1
ARG PY_VERSION=3.12

##
# Build stage: build and install dependencies
##
FROM python:${PY_VERSION} AS builder

ARG VERSION=0.dev
ENV PDM_BUILD_SCM_VERSION=${VERSION}

WORKDIR /project

# install PDM
RUN pip install -U pip setuptools wheel
RUN pip install pdm pdm-dockerize

RUN --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
    --mount=type=bind,source=pdm.lock,target=pdm.lock \
    --mount=type=cache,target=$HOME/.cache,uid=$UUID \
    pdm dockerize --prod -v

##
# Run stage: create the final runtime container
##
FROM python:${PY_VERSION} AS runtime

WORKDIR /app

# Fetch built dependencies
COPY --from=builder /project/dist/docker /app
# Copy needed files from your project (filter using `.dockerignore`)
COPY  . /app

ENTRYPOINT ["/app/entrypoint"]
CMD ["your-default-command"]

Using uv

When PDM is configured with use_uv = true, pdm-dockerize automatically uses uv pip install --target instead of the default pip-based installer for faster dependency installation. If uv is not found at runtime, it falls back gracefully to the pip-based installer.

Lockfiles generated by uv (which store extras as separate entries) are automatically handled.

Here is an example Dockerfile using uv:

# syntax=docker/dockerfile:1
ARG PY_VERSION=3.12

##
# Build stage: build and install dependencies
##
FROM python:${PY_VERSION} AS builder

ARG VERSION=0.dev
ENV PDM_BUILD_SCM_VERSION=${VERSION}

WORKDIR /project

# install uv, PDM and pdm-dockerize
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
RUN uv pip install --system pdm pdm-dockerize

# enable uv as PDM downloader
RUN pdm config use_uv true

RUN --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
    --mount=type=bind,source=pdm.lock,target=pdm.lock \
    --mount=type=cache,target=$HOME/.cache,uid=$UUID \
    pdm dockerize --prod -v

##
# Run stage: create the final runtime container
##
FROM python:${PY_VERSION} AS runtime

WORKDIR /app

# Fetch built dependencies
COPY --from=builder /project/dist/docker /app
# Copy needed files from your project (filter using `.dockerignore`)
COPY  . /app

ENTRYPOINT ["/app/entrypoint"]
CMD ["your-default-command"]

Command line options

pdm dockerize [OPTIONS] [TARGET]
Option Description
TARGET Output directory (default: dist/docker)
--prod / --production Select production dependencies only
-G GROUP / --group GROUP Select a specific dependency group (can be repeated)
--no-default Do not include the default dependencies
--dry-run Preview packages and entrypoint script without writing
-L LOCKFILE / --lockfile LOCKFILE Use an alternative lockfile

Selecting scripts

By default, the dockerize command will render a script without any command as it does not select any script by default.

You can select scripts with the include and exclude properties of the tool.pdm.dockerize section. Those properties are optional, can be either a string or list of string. Each string is a fnmatch filter pattern

Dockerize first select script based on the include patterns and then filter-out those matching with any exclude pattern.

Include all scripts

[tool.pdm.dockerize]
include = "*"

Include some specific scripts

[tool.pdm.dockerize]
include = ["my-script", "my-other-script"]

Include all scripts excluding those matching prefix-*

[tool.pdm.dockerize]
include = "*"
exclude = "prefix-*"

Include all scripts matching a prefix but two

[tool.pdm.dockerize]
include = "prefix-*"
exclude = ["prefix-not-you", "prefix-you-neither"]

Selecting binaries

By default, the dockerize command will not copy any python executable provided by your dependencies. You can select binaries with the include_bins and exclude_bins properties of the tool.pdm.dockerize section. Syntax and behavior are exactly the exact sames than include/exclude for script selection.

Include all python executables

[tool.pdm.dockerize]
include_bins = "*"

Include some specific executables

Most of the time, it will look like this:

[tool.pdm.dockerize]
include_bins = ["uvicorn"]

Generated entrypoint

The dockerize command generates a POSIX sh entrypoint script alongside the installed packages. This entrypoint exposes selected PDM scripts as subcommands. Here is what it does:

  • PYTHONPATH: automatically set to include lib/. For pdm.backend-based projects using src-layout, the package source directory is also added.
  • PATH: automatically set to include bin/, so any installed console-script binaries are available.
  • Pre/post scripts: if you define pre_<script> or post_<script> hooks in your PDM scripts, they are executed before and after the main script respectively.
  • {args} interpolation: the PDM {args} and {args:defaults} placeholder syntax is supported. Arguments passed to the entrypoint subcommand are forwarded through "$@". When no {args} placeholder is present, arguments are appended automatically.
  • Script kinds: all PDM script kinds are supported: cmd, call, shell, and composite.

Controlling environment

pdm-dockerize respects defined environment variables:

  • scripts env variables are properly set
  • shared _.env variables are properly set
  • scripts env_file are properly loaded
  • shared _.env_file are properly loaded

In addition, you can define some docker-only environment variables using the tool.pdm.dockerize.env table or some docker-only .env files using tool.pdm.dockerize.env_file

Defining docker-only environment variables

Those environment variables will only be effective in the docker entrypoint.

[tool.pdm.dockerize.env]
VAR = "value"

Loading docker-only environment files

This file will only be loaded in the docker entrypoint.

[tool.pdm.dockerize]
env_file = "docker.env"

Workspaces

PDM workspaces (requires pdm>=2.28) are supported.

Given a workspace root declaring its members:

my-workspace/
├── pyproject.toml          # [tool.pdm.workspace] members = ["packages/*"]
├── pdm.lock                # a single, shared lockfile
└── packages/
    ├── api/pyproject.toml
    └── worker/pyproject.toml
[tool.pdm.workspace]
members = ["packages/*"]

[tool.pdm.dockerize]
include = ["serve", "migrate"]
include_bins = "*"

[tool.pdm.scripts]
serve = "uvicorn api.main:app --host 0.0.0.0"
migrate = "alembic upgrade head"

Building a single image for the whole workspace

Running pdm dockerize from the workspace root installs every workspace member, along with their transitive dependencies, into the output directory, and exposes their console scripts (subject to include_bins/exclude_bins).

Members are installed as regular (non-editable) packages, so the resulting image is self-contained and never references build-time host paths.

A member is installed when it is matched by [tool.pdm.workspace] members, has a project.name and is a distribution (ie. neither distribution = false nor package-type = "application").

Members are treated as implicit default group dependencies, so they are excluded by selections dropping that group, such as --no-default.

Members must be present in the build stage so their wheels can be built: make sure your .dockerignore does not exclude the members directory.

COPY pyproject.toml pdm.lock ./
COPY packages/ ./packages/
RUN pdm dockerize

Building one image per member

Running pdm dockerize from a member, or targeting it with --project, builds an image for that member only:

$ cd packages/api && pdm dockerize
$ pdm --project packages/api dockerize    # equivalent
  • the member dependencies are installed into packages/api/dist/docker/lib, including the sibling members it depends on, as non-editable packages;
  • the members it does not depend on are not installed;
  • like any dockerized project, the member itself is not installed: its sources are exposed through PYTHONPATH, so they have to be in the image.
COPY packages/api /app
COPY packages/api/dist/docker /app
WORKDIR /app
ENTRYPOINT ["/app/entrypoint"]

Declare a dependency on a sibling member by name:

[project]
name = "api"
dependencies = ["shared"]

Where the configuration comes from

From the targeted project From the workspace root
tool.pdm.dockerize the pdm.lock lock file
tool.pdm.scripts the Python interpreter
the dependencies and the groups the tool.pdm.source package indexes
the output directory the use_uv setting

Nothing is inherited from the workspace root: the tool.pdm.dockerize and tool.pdm.scripts sections of the root are ignored when targeting a member, and vice versa. A member pdm.toml is ignored too.

[!NOTE] Selecting a group only defined in a member, using --group, is rejected: the workspace lock file only records the groups of the workspace root.

Internals

This plugin works by subclassing some pdm classes to reuse the installation process:

  • DockerizeEnvironment, a pdm PythonLocalEnvironment targeting the dockerize output directory
  • DockerizeInstallManager, a pdm InstallManager filtering binaries
  • DockerizeSynchronizer, a pdm Synchronizer using a DockerizeInstallManager as InstallManager
  • DockerizeUvSynchronizer, an alternative synchronizer that invokes uv pip install --target as a subprocess when use_uv is enabled
  • FilteringDestination, a pdm InstallDestination filtering binaries

This way, the dockerization reuses the same installation process, just tuned for docker and augmented with pdm-dockerize specifics.

Contributing

Read the dedicated contributing guidelines.

Download files

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

Source Distribution

pdm_dockerize-0.8.0.tar.gz (37.0 kB view details)

Uploaded Source

Built Distribution

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

pdm_dockerize-0.8.0-py3-none-any.whl (17.7 kB view details)

Uploaded Python 3

File details

Details for the file pdm_dockerize-0.8.0.tar.gz.

File metadata

  • Download URL: pdm_dockerize-0.8.0.tar.gz
  • Upload date:
  • Size: 37.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: pdm/2.28.0 CPython/3.12.3 Linux/6.17.0-1020-azure

File hashes

Hashes for pdm_dockerize-0.8.0.tar.gz
Algorithm Hash digest
SHA256 26107a9b6d2265312fd121e879307b3669f3558212b0f85827833f5d5804b216
MD5 ea98c485824e35de9341d321570be331
BLAKE2b-256 18a15c2cee57b377058fc73177def56f20071ee1516e36af0c4bb3a0c36ec743

See more details on using hashes here.

File details

Details for the file pdm_dockerize-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: pdm_dockerize-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 17.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: pdm/2.28.0 CPython/3.12.3 Linux/6.17.0-1020-azure

File hashes

Hashes for pdm_dockerize-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6e5102d64b36438c767d6dc4a6f51aef32133fca0f8067d9d80ff19e36f6491d
MD5 2b20e2a1639de2f605e0f0691d767b96
BLAKE2b-256 fae6fd41cd8896870eb9699a8db06cfa56bad479b9eda5f9362873f0ef3148c8

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