Write your first recipe in under five minutes
By the end of this page, a ten-line Python module renders this Dockerfile:
# syntax=docker/dockerfile:1
FROM ubuntu:24.04 AS base
ARG APP_TAG=v1.0.0
ENV APP_TAG=${APP_TAG}
WORKDIR /app
RUN echo "hello from docker-dsl" > /app/greeting.txt
CMD ["cat", "/app/greeting.txt"]The recipe greets a build argument and prints it at runtime, and the ARG value comes from a CLI flag — change the flag, get a different Dockerfile.
1. Write the recipe
Create minimal.py:
from __future__ import annotations
from docker_dsl import Stage
from docker_dsl import context as ctx
ctx.register("tag", str)
with Stage("ubuntu:24.04") as s:
s.arg("APP_TAG", ctx.tag or "latest", env=True)
s.workdir("/app")
with s.run() as r:
r.echo("hello from docker-dsl") > "/app/greeting.txt" # pyright: ignore[reportUnusedExpression]
s.cmd("cat", "/app/greeting.txt")Three things happen in that file:
ctx.register("tag", str)declares a config field namedtag. Every registered field is required when you render.with Stage("ubuntu:24.04") as s:opens a stage. Each method call inside the block adds one instruction to it.s.arg(..., env=True)writes both anARGand anENV, so the value is available at build time and runtime.
2. Render it
Run the recipe through the CLI, passing the tag field:
uvx docker-dsl minimal --tag=v1.0.0 --out Dockerfile
TipCheckpoint
Dockerfile in your working directory now matches the output at the top of this page. A ModuleNotFoundError means the recipe isn’t in your working directory — run the command from the directory holding minimal.py.
Omit --out to print to stdout instead. Change --tag and render again to get a different Dockerfile from the same recipe.
Where to go next
- Render from the CLI covers every render option, plus rendering from Python.
- The run builder composes a multi-command
RUN. - Multi-stage builds copies artifacts between stages.