A versatile Docker image builder that uses Go Templates extended with Sprig functions to dynamically generate Dockerfiles, validate configurations, and build container images efficiently. The app supports parameterized builds, parallel execution, and customization for streamlined container development.
A CLI tool for building Docker images with configurable Dockerfile templates and multi-threaded execution.
When 'docker build' is just not enough. :-)
Usage:
td [flags]
Flags:
-b, --build Build Docker images after templating
-c, --config string Path to the configuration file (required)
-d, --delete Delete templated Dockerfiles after successful building
-e, --engine string Select the container engine to use: docker or buildx (default "docker")
-h, --help help for td
-i, --image string Limit the build to a single image
--parallel int Specify the number of threads to use, defaults to number of CPUs (default 20)
-p, --push Push Docker images after building
-s, --squash Squash images to reduce size (experimental)
-t, --tag string Tag to use as the image version
-v, --verbose Increase verbosity of output
-V, --version Display the application version and exit
Download latest version of file:
sudo curl -sLfo /usr/local/bin/td https://github.com/tgagor/template-dockerfiles/releases/latest/download/td-linux-amd64
sudo chmod +x /usr/local/bin/td
Alternatively, extract it to any location under PATH
. Or download with Go Lang:
go install github.com/tgagor/template-dockerfiles/cmd/td@latest
Ensure you have GOPATH
, in your PATH
.
For complete examples of configuration check example directory.
Start defining your build configuration file. It's a playbook by which your Docker image templates will be generated and build:
registry: repo.local
prefix: my-base
maintainer: Awesome Developer <awesome@mail>
images:
jdk:
dockerfile: jdk/Dockerfile.tpl
variables:
alpine:
- "3.19"
- "3.20"
java:
- 11
- 17
- 21
tags:
- jdk:{{ .tag }}-{{ .java }}-alpine{{ .alpine }}
- jdk:{{ .java }}-alpine{{ .alpine | splitList "." | first }}
Call build like:
td --config build.yaml --tag v1.2.3 --build --delete
Which will produce 2x3 -> 6 images, with 12 labels:
repo.local/my-base/jdk:v1.2.3-11-alpine3.19
repo.local/my-base/jdk:11-alpine3
repo.local/my-base/jdk:v1.2.3-17-alpine3.19
repo.local/my-base/jdk:17-alpine3
repo.local/my-base/jdk:v1.2.3-21-alpine3.19
repo.local/my-base/jdk:21-alpine3
repo.local/my-base/jdk:v1.2.3-11-alpine3.20
repo.local/my-base/jdk:11-alpine3
repo.local/my-base/jdk:v1.2.3-17-alpine3.20
repo.local/my-base/jdk:17-alpine3
repo.local/my-base/jdk:v1.2.3-21-alpine3.20
repo.local/my-base/jdk:21-alpine3
Order of values under variables
block is used to determine the order of labels creation.
This file format defines the configuration for dynamically generating Docker images using Jinja2 templates. It specifies global settings, image definitions, and build parameters.
- Description: The Docker registry to which images will be pushed. Skip to use Docker Hub.
- Type: String
- Example:
registry: repo.local
- Description: A prefix applied to all image names for organizational purposes. Might be a Docker Hub user name.
- Type: String
- Example:
prefix: my-base
- Description: The maintainer's name and contact information.
- Type: String
- Example:
maintainer: Name <email@domain>
- Description: Global labels that would be added to each image automatically.
- Type: Dictionary of strings
- Example:
labels: - org.opencontainers.image.licenses: License(s) under which contained software is distributed as an [SPDX License Expression](https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/). - org.opencontainers.image.title: Human-readable title of the image (string). - org.opencontainers.image.description: | Human-readable description of the software packaged in the image. (multiline string).
- Notes:
- I recommend to follow OCI Label Schema, app will add some of them automatically.
- Even those labels can be templated, but as they're global, you should only use variables available in all images. Otherwise they might be evaluated to:
<no value>
, unless you filter those out with additional conditions.
Defines the Docker images to build. Each image has specific attributes such as its Dockerfile, variables, and labels. Images are build in order, top-down, which allows to construct dependencies between images.
Each image is identified by a key (e.g., base
, jdk
, jre
) and contains the following attributes:
- Description: The path to the Dockerfile template used to build the image. Go Templates extended with Sprig functions are supported.
- Type: String
- Example:
dockerfile: base/Dockerfile.tpl
-
Description: A dictionary of variables used to parameterize the Dockerfile template.
-
Type: Dictionary of lists
-
Example:
variables: alpine: - "3.20" - "3.19"
-
Notes:
- Builder generates a Cartesian product of all variables (all combinations).
- The variables can have multiple values, allowing builds for different configuration sets.
- Variables are substituted into the template during build.
- Description: A list of names and tags to tag the generated Docker images.
- Type: List of strings
- Example:
labels: - base:{{ .tag }}-alpine{{ .alpine }} - base:alpine{{ .alpine }}
- Notes:
- Labels support Go Templates extended with Sprig functions. For example,
{{ .alpine | splitList "." | first }}
extracts the major version fromalpine
. tag
argument is provided by--tag
/-t
parameter, which reflects the image version.
- Labels support Go Templates extended with Sprig functions. For example,
- Description: Per image labels, that would be added to each image.
- Type: Dictionary of strings
- Example:
labels: - org.opencontainers.image.base.name: alpine:{{ .alpine }} - org.opencontainers.image.description: | Human-readable description of the software packaged in the image. (multiline string).
- Notes:
- I recommend to follow OCI Label Schema, app will add some of them automatically.
- Labels can be templated and they will override global labels of same name.
For multi-platform builds, you need to prepare your build environment. This guide uses QEMU emulation, which provides a broad list of platforms available out of the box.
Building multi-platform images requires support from base images and tools that work on specific platforms. Verify compatibility before you start.
This example uses Ubuntu, but the steps should be similar on other platforms.
-
Update your package list and install QEMU:
sudo apt update sudo apt-get install -y qemu-system
-
Use the tonistiigi/binfmt image to install QEMU and register the executable types on the host. This allows the
buildx
builder to recognize available target platforms:docker run --privileged --rm tonistiigi/binfmt --install all
-
Verify the installation with:
docker buildx ls
You should see output similar to:
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS default* docker \_ default \_ default running v0.17.3 linux/amd64 (+3), linux/arm64, linux/arm (+2), linux/ppc64le, (3 more)
-
Add new builder
docker buildx create --use --name multi-arch-builder
To enable the containerd snapshotters feature, follow these steps:
-
Add the following configuration to your
/etc/docker/daemon.json
file:{ "features": { "containerd-snapshotter": true } }
-
Save the file.
-
Restart the Docker daemon for the changes to take effect:
sudo systemctl restart docker
-
After restarting the daemon, verify that you're using containerd snapshotter storage drivers:
docker info -f '{{ .DriverStatus }}'
You should see output similar to:
[[driver-type io.containerd.snapshotter.v1]]
Now it's time to add required platforms to your configuration, you can put them in the global scope or per image, for example:
platforms:
- linux/amd64
- linux/arm64
images:
base:
dockerfile: base/Dockerfile.tpl
...
jre:
dockerfile: jre/Dockerfile.tpl
# overwrite platforms for this image only
platforms:
- linux/amd64
You can also use few variables in templates that would refer to your current platform, like:
BUILDPLATFORM
— matches the current machine. (e.g. linux/amd64)BUILDOS
— os component of BUILDPLATFORM, e.g. linuxBUILDARCH
— e.g. amd64, arm64, riscv64BUILDVARIANT
— used to set ARM variant, e.g. v7TARGETPLATFORM
— The value set with --platform flag on buildTARGETOS
- OS component from --platform, e.g. linuxTARGETARCH
- Architecture from --platform, e.g. arm64TARGETVARIANT
-
Check your images, for multiple platforms, with:
docker image inspect \
--format "{{.ID}} {{.RepoTags}} {{.Architecture}}" \
$(docker image ls -q)
images
: At least one image must be defined with a validdockerfile
.
- Use the
prefix
field for consistent image organization. - Add meaningful labels to enhance discoverability and traceability.
- Keep in mind that order of variables, determine order of labeling and some labels might overwrite previously created.
- Tool detects number of available CPU and run as many jobs as possible.
- For debugging, it might be easier to use
--parallel 1 --verbose
to limit amount of messages produced.
- Use
--verbose
flag. It will produce a lot of debug information. - Without
--build
flag, script will just template Dockerfiles, so you can check them for correctness. - Use
--image
to build just single set of images, instead of building them all.
Use Go Templates support by Sprig functions to create dynamic expressions like {{ .alpine | splitList "." | first }}
to generate tags or labels dynamically.
Same approach as for tags applies to labels. You can template them as you whish.
All the variables available for templating:
registry
- If provided.prefix
- If provided.maintainer
- If provided.tag
- If--tag
flag used.image
- A key fromimages
in the config file, useful for conditions.labels
- Some generated automatically, then from "global scope" (at the top of config file), merged with "per image" labels,- And finally, whatever you define in
variables
blocks.