Skip to content

Commit 55b02c7

Browse files
authored
chore: add blog post about building images with bake (#314)
* chore: add blog post about building images with bake Authored-by: Jonathan Gonzalez V. <jonathan.abdiel@gmail.com> Signed-off-by: Jaime Silvela <jaime.silvela@mailfence.com> Signed-off-by: Floor Drees <floordrees@gmail.com>
1 parent 3ea7806 commit 55b02c7

File tree

2 files changed

+200
-0
lines changed

2 files changed

+200
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
extensions = [
2+
"pgvector",
3+
]
4+
target "myimage" {
5+
dockerfile-inline = <<EOT
6+
ARG BASEIMAGE="ghcr.io/cloudnative-pg/postgresql:16.9-standard-bookworm"
7+
FROM $BASEIMAGE AS myimage
8+
ARG EXTENSIONS
9+
USER root
10+
RUN apt-get update && \
11+
apt-get install -y --no-install-recommends $EXTENSIONS && \
12+
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false && \
13+
rm -rf /var/lib/apt/lists/* /var/cache/* /var/log/*
14+
USER 26
15+
EOT
16+
matrix = {
17+
tgt = [
18+
"myimage"
19+
]
20+
pgVersion = [
21+
"16.9",
22+
"17.5",
23+
]
24+
}
25+
name = "postgresql-${index(split(".",cleanVersion(pgVersion)),0)}-standard-bookworm"
26+
target = "${tgt}"
27+
args = {
28+
BASE_IMAGE = "ghcr.io/cloudnative-pg/postgresql:${cleanVersion(pgVersion)}-standard-bookworm",
29+
EXTENSIONS = "${getExtensionsString(pgVersion, extensions)}",
30+
}
31+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
---
2+
title: "Creating a custom container image for CloudNativePG v2.0"
3+
date: 2025-07-23
4+
draft: false
5+
author: jgonzalez
6+
tags:
7+
- blog
8+
- information
9+
- programming
10+
- applications
11+
- containers
12+
- postgresql
13+
- postgres
14+
- images
15+
- tutorial
16+
- bake
17+
- docker
18+
summary: Using Docker's Bake to create container images for the CloudNativePG Operator v2.0.
19+
---
20+
21+
## Summary
22+
Nearly two years ago, we shared a [blog post on building custom container
23+
images for CloudNativePG]({{% ref "/blog/creating-container-images/" %}}). Since then, the container ecosystem has evolved
24+
significantly—one notable development being the introduction of [Docker Bake]((https://docs.docker.com/build/bake/)).
25+
26+
Docker Bake simplifies image builds using a straightforward configuration file,
27+
and it’s now our recommended approach for building CloudNativePG images.
28+
29+
In this post, we’ll walk through a simple baking recipe to create a custom
30+
container image. With Bake, you can also easily build multiple images in
31+
parallel.
32+
33+
## Ingredients
34+
35+
- A Bake file, using the one provided in the [CloudNativePG repository](https://github.com/cloudnative-pg/postgres-containers/blob/main/docker-bake.hcl) as a base.
36+
- A second, local Bake file to override the base configuration—this lets you apply your custom changes and build the container images accordingly.
37+
38+
## Instructions
39+
40+
### Step 1: Prepare local Bake file
41+
42+
To build a custom image we add the following content in a local file with name `bake.hcl`:
43+
44+
```hcl
45+
extensions = [
46+
"pgvector",
47+
]
48+
target "myimage" {
49+
dockerfile-inline = <<EOT
50+
ARG BASEIMAGE="ghcr.io/cloudnative-pg/postgresql:16.9-standard-bookworm"
51+
FROM $BASEIMAGE AS myimage
52+
ARG EXTENSIONS
53+
USER root
54+
RUN apt-get update && \
55+
apt-get install -y --no-install-recommends $EXTENSIONS && \
56+
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false && \
57+
rm -rf /var/lib/apt/lists/* /var/cache/* /var/log/*
58+
USER 26
59+
EOT
60+
61+
matrix = {
62+
tgt = [
63+
"myimage"
64+
]
65+
pgVersion = [
66+
"16.9",
67+
"17.5",
68+
]
69+
}
70+
name = "postgresql-${index(split(".",cleanVersion(pgVersion)),0)}-standard-bookworm"
71+
target = "${tgt}"
72+
args = {
73+
BASE_IMAGE = "ghcr.io/cloudnative-pg/postgresql:${cleanVersion(pgVersion)}-standard-bookworm",
74+
EXTENSIONS = "${getExtensionsString(pgVersion, extensions)}",
75+
}
76+
}
77+
```
78+
79+
There are a few important points to highlight:
80+
81+
- The `extensions` variable is a list of extensions that we want to include in the image. In our recipe we are using
82+
`pgvector`, but you can add others as needed.
83+
- The `dockerfile-inline` variable contains our Dockerfile definition, which cannot be used remotely. We will explain
84+
why later.
85+
- The `target` and the `tgt` values share the same name, but you can use any name you prefer.
86+
- The `pgVersion` variable is a list specifying the PostgreSQL version(s) in MAJOR.MINOR format.
87+
- The `name` field is used to identify individual entries in the matrix we’ve defined.
88+
- The `args` variable contains the arguments passed to the Dockerfile—more on this later.
89+
- The `getExtensionsString()` function is inherited from the base Bake file mentioned in the [Ingredients](#ingredients) section
90+
91+
92+
### Step 2: Build the image
93+
94+
We can now build the image using the following command:
95+
96+
```bash
97+
docker buildx bake -f docker-bake.hcl -f cwd://bake.hcl "https://github.com/cloudnative-pg/postgres-containers.git" myimage
98+
```
99+
100+
This will build the image for the bake matrix we previously created, and will try to push the image to the registry at
101+
`localhost:5000`, which is the default registry defined for testing environments in the parent Bake file. Let's explain
102+
the full command:
103+
104+
As outlined in the [Bake documentation on remote definitions](https://docs.docker.com/build/bake/remote-definition/), you can use a remote Bake file that includes
105+
functions and default targets, then attach a local Bake file to override any default values as needed.
106+
107+
In the command above, `-f cwd://bake.hcl` is the local file that we created in Step 1, and
108+
`-f docker-bake.hcl` is the remote file in the git repo, that we're using to build the image.
109+
110+
You can explore more about all the content generated and used inside the Bake file by appending the `--print` flag, as
111+
in the following command:
112+
113+
```bash
114+
docker buildx bake -f docker-bake.hcl -f cwd://bake.hcl "https://github.com/cloudnative-pg/postgres-containers.git" myimage --print
115+
```
116+
117+
### Step 3: Push the image to a registry
118+
119+
Now you just need to push the image to a registry. You can do this by using the following command:
120+
121+
```bash
122+
registry=your/registry:5000 docker buildx bake -f docker-bake.hcl -f cwd://bake.hcl "https://github.com/cloudnative-pg/postgres-containers.git" myimage --push
123+
```
124+
125+
The previous command will push the images in the following format: `your/registry:5000/postgresql-testing:17-standard-bookworm`.
126+
Using the `--print` flag you can explore the full list of tags created that are in the parent Bake file.
127+
128+
### Step 4: Serve the image
129+
130+
You can now use the image that we've built for your clusters.
131+
132+
## Deep dive into the Bake and Dockerfile
133+
134+
The simplicity of Bake to do even more stuff is amazing, and allows you to create custom images easily.
135+
136+
### Bake file
137+
138+
The magic starts with our [postgres-containers repository](https://github.com/cloudnative-pg/postgres-containers),
139+
where we have a `docker-bake.hcl` file that is being used to build the images for the CloudNativePG project.
140+
It's the base for our custom Bake file.
141+
142+
The `docker-bake.hcl` file contains a lot of functions that are used to build the images. One of them is the `getExtensionsString()`.
143+
This function, given the list of extensions we provided, will return a string of the extensions with the correct package name
144+
for a Debian-based distribution, in our case, Debian Bookworm.
145+
For example, the `pgvector` extension will be translated into
146+
`postgresql-16-pgvector`, which is the name of the package for pgvector extensions for PostgreSQL 16 in the Debian
147+
Bookworm distribution.
148+
149+
When we add elements to, for example, the `args` variable, those elements are processed by the Docker bake command, and will be
150+
merged, meaning that the new elements will be added, and the existing ones will be overwritten.
151+
152+
### Dockerfile file
153+
154+
The Dockerfile is defined as a heredoc string due to Bake's limitation in overriding a remote Dockerfile with a local
155+
one. However, this approach still lets us modify the FROM directive, allowing us to base our image directly on the
156+
CloudNativePG images and add only the extensions we need—without rebuilding everything.
157+
158+
## Making your images for specific architectures
159+
160+
By default, images are built for both `amd64` and `arm64` architectures, which is the recommended setup for most users.
161+
However, if you want to build images only for one specific architecture, saving some space, you can override the
162+
`platforms` variable in your local Bake file.
163+
164+
```hcl
165+
platforms = ["linux/amd64"]
166+
```
167+
168+
If you’d like to build everything into your own repository while managing the same tags, that’s also possible.
169+
We may cover that in a future post.

0 commit comments

Comments
 (0)