# image-template: the bootc starting point for a custom Universal Blue image

> The ublue-os image-template repository is the recommended way to make customisations to any image published by the Universal Blue Project, using bootc so the result is an OCI image you switch to with a single command. The build is signed with cosign, so the setup fails until you generate a key pair, and the practical work is choosing a base image and changing two variables.

**ublue-os/image-template** — Build your own custom Universal Blue Image!

- Repository: https://github.com/ublue-os/image-template
- Stars: 886 · Forks: 189
- Language: Just
- License: Apache-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/ublue-os-image-template

## A template for bootc images, not a config to edit in place

The repository is meant to be a template for building your own custom bootc image, and the recommended way to make customisations to any image published by the Universal Blue Project. The distinction matters, because you do not fork and edit this repository in place, you copy it. The mechanism is the Use this Template button on the repository page, and the instruction is that you can set the name and description of your repository as you like but all other settings should be left untouched. After the copy you have to enable the GitHub Actions workflows for the new repository, by going to the Actions tab and clicking the button to enable workflows, which is the same git and GitHub familiarity the whole process assumes. The prerequisites are a GitHub account, a machine running a bootc image such as Bazzite, Bluefin, Aurora or Fedora Atomic, and experience installing and using CLI programs. The primary language recorded for the project is Just, which refers to the command runner file at the root, and the topics are bootc and oci.

## The build fails until you have a signing key

Step two is the one that stops people, and it is by design. Container signing is important for end-user security and is enabled on all Universal Blue images, and by default the image builds will fail if you do not set it up. You install the cosign CLI tool, and then inside your repository folder you generate a key pair:

```bash
COSIGN_PASSWORD="" cosign generate-key-pair
```

The empty password is required, because the signing key is used in GitHub Actions and will not work if it is password protected. The warning that follows is the serious part: be careful to never accidentally commit cosign.key into your git repo, because if this key goes out to the public the security of your repository is compromised. The key then has to be added to GitHub as a secret, and there are two documented ways. The preferred one is the web interface, going to the repository settings, under Secrets and Variables, then Actions, adding a new secret named SIGNING_SECRET and pasting the contents of cosign.key into it, making sure it is the .key file and not the .pub file. The alternative, if you have the github-cli installed, is:

```bash
gh secret set SIGNING_SECRET < cosign.key
```

The redirect is what keeps the private key out of your shell history and out of the command line.

## Choosing a base image is a FROM line and a decision to maintain it

Choosing the base image means modifying the line in the container file starting with FROM, and that line is the image your image derives from and your starting point for modifications. You can pick any of the Universal Blue images or start from a Fedora Atomic system, and the list given is non-exhaustive. Bazzite is ghcr.io/ublue-os/bazzite:stable, Aurora is ghcr.io/ublue-os/aurora:stable, Bluefin is ghcr.io/ublue-os/bluefin:stable, the Universal Blue base is ghcr.io/ublue-os/base-main:latest, and Fedora is quay.io/fedora/fedora-bootc:44. More Universal Blue images are on the packages page. The advice for people who are unsure is to choose the one your system is currently on for a smooth transition, and to find out what that is you run:

```bash
sudo bootc status
```

The image you are on is displayed after Booted image, and you paste that after the FROM statement. The consequence of the choice is the thing to think about, because a base image is a maintenance commitment: every upstream update to it becomes an update to yours, and the more layers you put on top, the more you have to retest.

## Two variables and one commit is the whole customisation

The renaming step is short. You change the IMAGE_NAME and REPO_ORGANIZATION variable inside the image-template.env file, and then you commit and push everything that changed in step two, which is the Containerfile, the env file and the public key:

```bash
git add Containerfile image-template.env cosign.pub
git commit -m "Initial Setup"
git push
```

The public key goes in and the private key stays out, which is the whole point of the earlier warning. Once pushed you go to the Actions tab on your repository page, and a green checkmark on the top commit means your new image is ready. The naming of the env file is where the image identity lives, so the two variables are what determine the name your image is published under, and the workflow is configured so that by default the image name matches the GitHub repository name, which means the two have to agree.

## Switching to your image is one command and a reboot

From a bootc system, you switch to your image with a single command, substituting in your GitHub username and image name where noted:

```bash
sudo bootc switch ghcr.io/<username>/<image_name>
```

This queues the image for the next reboot, which you can do immediately after the command finishes, and that is the mechanism that makes bootc images practical. Because the image is an OCI artifact rather than a disk image, a switch is a download and a change of what boots, and a failed image is a matter of choosing the previous one at the next reboot rather than a reinstall. The template also provides an out of the box workflow for creating disk images, that is ISO, qcow and raw, for your custom OCI image, which can then be used to install directly onto machines, so the same source produces both a container artifact and something you can write to a USB stick. The repository holds the supporting pieces for that, with a disk_config directory and a build_files directory alongside the Containerfile and the env file.

## The three files that make up the build

The repository contents section is short and describes the division of labour. The Containerfile defines the operations used to customise the selected image, it is the entry point for your image build, and it works exactly like a regular podman Containerfile, with the podman documentation linked for reference. The build.sh file is called from your Containerfile, and it is the best place to install new packages or make any other customisation to your system, with customisation examples contained within it. The build.yml GitHub Actions workflow creates your custom OCI image and publishes it to the GitHub Container Registry, and by default the image name matches the GitHub repository name. The last two entries are the system_files directory and the artifacthub-repo.yml, which is the metadata file that lets the resulting image show up in a software catalogue such as Artifact Hub with a description and links. There is also a Justfile at the root, which is where the local task commands live.

## Where to ask when the instructions are not enough

The project points at three communities, and the distinction between them is worth knowing. The Universal Blue forums and the Universal Blue Discord are the managed spaces for questions about this template after following the instructions. The bootc discussion forums are described as not a Universal Blue managed space but an excellent resource if you run into issues with building bootc images, which is the right place for a question about bootc itself rather than about the template. There is also a video tutorial embedded in the README, credited to TesterTech, for people who would rather watch the steps than read them, and the quick start is the instruction to read and follow the steps in the next few headings. The licence is Apache-2.0, the last push was on 2026-10-12, and the repository has no GitHub releases, so the template is consumed as a copy rather than as a versioned dependency.

## Conclusion

image-template suits a developer who wants their own version of a Universal Blue or Fedora Atomic image, built the same way the upstream images are, with signing and disk image generation already wired up. It does not suit a team that just wants to install packages on the machine they have, since that needs no image build at all. Before you start, read the signing requirement, because the build fails without a cosign key pair and the private key must never end up in the repository, and check that the base image you pick is one you actually want to maintain, since every update to it becomes an update to yours.

## FAQ

### What is the ublue-os image-template repository for?

It is a template for building your own custom bootc image, and the recommended way to make customisations to any image published by the Universal Blue Project. You copy it with the Use this Template button, leave the other settings untouched, and enable the GitHub Actions workflows in your new repository.

### Why does the image build fail before I set up signing?

Container signing is enabled on all Universal Blue images and the build fails by default without it. You generate a key pair with cosign generate-key-pair using an empty password, then add the private key to GitHub as a secret named SIGNING_SECRET, because the key is used in GitHub Actions and does not work if it is password protected.

### How do I pick a base image in the image-template?

You change the line in the Containerfile starting with FROM. You can use any Universal Blue image or a Fedora Atomic base, and the suggested approach if you are unsure is to pick the one your system already runs, which you can find with sudo bootc status and reading the value after Booted image.

### How do I switch my machine to the custom image?

Run sudo bootc switch ghcr.io/<username>/<image_name> from a bootc system, which queues the image for the next reboot, and reboot when it finishes. The template also ships a workflow that builds ISO, qcow and raw disk images from the same source if you need something you can write to a USB stick.

## Sources

- [Issues](https://github.com/ublue-os/image-template/issues)
- [License: Apache-2.0](https://github.com/ublue-os/image-template/blob/main/LICENSE)
- [README](https://github.com/ublue-os/image-template/blob/main/README.md)
- [ublue-os/image-template on GitHub](https://github.com/ublue-os/image-template)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ublue-os-image-template
