This tutorial will walk you through the process of creating a custom Linux image using the Builder tool.
We will start with the Builder example repository and build a feature to add an nginx HTTP server to our image.
Let's begin by creating a new GitHub repository based on the Builder example repository using this link:
https://github.com/new?template_name=builder_example&template_owner=gardenlinux
This repo has a GitHub Actions workflow enabled, so it will already start building the image on GitHub's hosted runners.
To customize the image, clone the repo locally:
git clone https://github.com/your_username/my_linux_image
cd my_linux_imageTo ensure that your local Podman installation is working correctly, you can test it by running the following command:
./build baseThis command will create a bootable Debian Forky disk image at .build/base-amd64-forky-6f72b564.raw (note that the commit may have changed since the time of writing).
You can test run the image using QEMU:
qemu-system-x86_64 -m 2048 -nodefaults -display none -serial mon:stdio -drive if=pflash,unit=0,readonly=on,format=raw,file=/usr/share/OVMF/OVMF_CODE.fd -drive if=virtio,format=raw,file=.build/base-amd64-forky-6f72b564.rawNow that we have verified that everything is working correctly, let's proceed to build our own feature.
- Create a directory called
nginxinside thefeaturesdirectory:
mkdir features/nginxThis is where our nginx feature will live. Features are a concept of the builder that allows us to build variants of images. For example, if we wanted to add an alternative HTTP server later, we could add an
apacheHttpdfeature. At image build time, we could pick if we want thenginxor theapacheHttpdfeature.
- Create a file named
info.yamlinsidefeatures/nginxand edit it with the content below:
description: HTTP server using Nginx
type: element
features:
include:
- baseThe
info.yamlfile is required for each feature by the builder. We'll specify that ournginxfeature includes thebasefeature. This makes sense because thenginxfeature on its own does not contain a full operating system, so to get a bootable image we include the debian system as it is defined inbase. See features.md for detailed information on the structure of features.
- Create a file named
pkg.includeinsidefeatures/nginxwith the following content:
nginx
pkg.includeis a list of packages this feature needs, each feature on a new line.
- Create a file named
exec.configinsidefeatures/nginxwith the following content:
#!/usr/bin/env bash
set -eufo pipefail
systemctl enable nginx
exec.configis a shell script we can use to customize our image. In this case, we enable the systemd unit for nginx which makes nginx start on boot.
- Make the
exec.configfile executable:
chmod +x features/nginx/exec.config- Create a directory named
/var/www/htmlinside thefile.includedirectory of Nginx:
mkdir -p features/nginx/file.include/var/www/htmlThe
file.includedirectory allows us to merge files and directories into the root filesystem of our image.
- Create a dummy
index.htmlfile insidefeatures/nginx/file.include/var/www/htmlwith content like the following (or customize it as desired):
<!DOCTYPE html>
<html>
<body>
<p>Hello World!</p>
</body>
</html>To test your feature, build the image using the following command:
./build nginxYou can then run the image with QEMU using the following command:
qemu-system-x86_64 -m 2048 -nodefaults -display none -serial mon:stdio -drive if=pflash,unit=0,readonly=on,format=raw,file=/usr/share/OVMF/OVMF_CODE.fd -drive if=virtio,format=raw,file=.build/nginx-amd64-forky-local.raw -netdev user,id=net0,hostfwd=tcp::8080-:80 -device virtio-net-pci,netdev=net0If everything worked as intended, you should see the system boot up. Once the system is booted, opening http://localhost:8080 in a browser should display the "Hello World!" message.
To also build the new image on GitHub Actions, we'll need to modify the .github/workflows/build.yml file.
Let's change the build step to include the nginx feature we just created, and let's upload our built image to GitHub's artifact storage:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 181a646..9e4261e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -13,4 +13,8 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build the image
- run: ./build base
+ run: ./build nginx
+ - uses: actions/upload-artifact@v4
+ with:
+ name: my-linux-image
+ path: .build/Now commit and push your changes and GitHub will build the image for you.
Congratulations! You have successfully created your first feature for the Builder and setup a CI Pipeline to build the image. 🎉