Skip to content
4 changes: 4 additions & 0 deletions blog/2020-10-29-supervisor-update.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ authorTwitter: ludeeus
title: "Upcoming changes to add-ons"
---

:::note
The builder action described in this post (`home-assistant/builder@master`) is a legacy workflow. It has since been replaced by dedicated composite actions. See the [builder migration post](/blog/2026/04/02/builder-migration) for the current recommended approach.
:::

## GitHub Action

You can now use our [builder][marketplace] as a [GitHub action][github_action]! :tada:
Expand Down
85 changes: 85 additions & 0 deletions blog/2026-04-02-builder-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
author: Jan Čermák
authorURL: https://github.com/sairon
authorImageURL: https://avatars.githubusercontent.com/u/211416?s=96&v=4
title: "Migrating app builds to Docker BuildKit"
---

The legacy `home-assistant/builder` container and the old `home-assistant/builder` GitHub Action have been retired. We recommend to migrate all app (formerly add-on) repositories to allow direct build with Docker BuildKit and to use the new composite actions described in this post.

## What changed and why

The old builder ran every architecture build inside a single privileged Docker-in-Docker container using QEMU emulation. This was slow, required elevated privileges, and those who were already familiar with Docker needed to learn how to use the custom Home Assistant's builder container. This also had unnecessary maintenance overhead as the builder can be currently fully replaced with Docker BuildKit, which is natively supported on GitHub Actions runners and has built-in multi-arch support with QEMU emulation when needed.

For your CI, the replacement is a set of focused [composite GitHub Actions](https://github.com/home-assistant/builder) that delegate building to the runner's native Docker with Docker BuildKit. Outside the CI, the migration means that your `Dockerfile` is now the single source of truth for building your app image, and you can use `docker build` directly to build and test your app locally without needing to use the builder container.

## Migration process

### Before (legacy action)

Previously, you may have had a single step in your GitHub Actions workflow that looked like this:

```yaml
- name: Build
uses: home-assistant/builder@master
with:
args: |
--all \
--target /data \
--docker-hub my-org \
--docker-user my-org \
--docker-password ${{ secrets.DOCKER_PASSWORD }}
```

### After (composite actions)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Before/After is a bit weird, especially since the After then goes on mostly talking about build.yaml migration.

To me a structure like this would make more sense:

## Migration process

### Update Dockerfiles

Move content from `build.yaml` to Dockerfile....

### Update GitHub action workflows

Replace the previous action `home-assistant/builder@master` with... 

I think its also not ideal that we have the YAML of the old actions in the post, but the new ones are just linked 😅 I'd not even show the old yaml at all, people copy stuff without reading, and it doesn't add that much value. I'd just mention that home-assistant/builder@master steps need to go, and then link to the new code.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!


Replace the above with the three composite actions. See the [example workflow](https://github.com/home-assistant/apps-example/blob/main/.github/workflows/builder.yaml) in our example app repository for a complete working example. Alternatively, use the [individual actions](https://github.com/home-assistant/builder?tab=readme-ov-file#example-workflow) in a more custom workflow as needed.


The new build workflow does not read `build.yaml`. You can remove the file from your repository. Its two commonly used sections should be migrated as follows:

- **`build_from`** - replace the `build_from` key in `build.yaml` with a `FROM` statement in your `Dockerfile`:

```dockerfile
FROM ghcr.io/home-assistant/base:latest
```

As the base images are now published as multi-platform manifests, there is usually no need to define per-arch base images anymore. The `build-image` action still supplies `BUILD_ARCH` as a build argument though, so you can use that in your `Dockerfile` if you need to use it in the template for the base image name.

- **`labels`** - move any custom Docker labels directly into your `Dockerfile` with a `LABEL` statement, with `io.hass.type="addon"` as a minimum to ensure the image is recognized as an app by Supervisor:

```dockerfile
LABEL \
io.hass.type="addon" \
org.opencontainers.image.title="Your awesome app" \
org.opencontainers.image.description="Description of your app." \
org.opencontainers.image.source="https://github.com/your/repo" \
org.opencontainers.image.licenses="Apache License 2.0"
```

- **`args`** - move custom build arguments into your `Dockerfile` as `ARG` definitions with default values:

```dockerfile
ARG MY_BUILD_ARG="default-value"
```

Default values in `ARG` replace what was previously supplied via `build.yaml`'s `args` dictionary. They can still be overridden at build time with `--build-arg` if needed.

### Image naming

The preferred way to reference a published app image is now the **generic (multi-arch) name** without an architecture prefix:

```yaml
# config.yaml
image: "ghcr.io/my-org/my-app"
```

The `{arch}` placeholder (e.g. `ghcr.io/my-org/{arch}-my-app`) is still supported as a compatibility fallback, but it's encouraged to use the generic name and let the manifest handle the platform resolution.

### Local builds

After updating your `Dockerfile`, you can use `docker build` to build the app image directly - you can refer to [Local app testing](/docs/apps/testing) for more details.

## Apps built locally by Supervisor

For backward compatibility, Supervisor still reads `build.yaml` file if it's present and populates the image build arguments with values read from this file. This will produce warnings and eventually be removed in the future, so it's recommended to migrate to the new Dockerfile-based approach as described above.
2 changes: 1 addition & 1 deletion docs/apps.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Useful links:
- [Home Assistant Supervisor](https://github.com/home-assistant/supervisor)
- [Home Assistant Core Apps](https://github.com/home-assistant/addons)
- [Home Assistant Docker base images](https://github.com/home-assistant/docker-base)
- [Home Assistant Builder](https://github.com/home-assistant/builder)
- [Home Assistant Builder actions](https://github.com/home-assistant/builder)
- [Home Assistant community apps](https://github.com/hassio-addons)
- [Home Assistant Operating System](https://github.com/home-assistant/operating-system)
- [Home Assistant Docker images](https://github.com/home-assistant/docker)
45 changes: 11 additions & 34 deletions docs/apps/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ addon_name/
translations/
en.yaml
apparmor.txt
build.yaml
CHANGELOG.md
config.yaml
DOCS.md
Expand All @@ -21,7 +20,7 @@ addon_name/
```

:::note
Translation files, `config` and `build` all support `.json`, `.yml` and `.yaml` as the file type.
Translation files and `config` support `.json`, `.yml` and `.yaml` as the file type.

To keep it simple all examples use `.yaml`
:::
Expand Down Expand Up @@ -58,7 +57,7 @@ then there will be a variable `TARGET` containing `beer` in the environment of y
All apps (formerly known as add-ons) are based on the latest Alpine Linux image. Home Assistant will automatically substitute the right base image based on the machine architecture. Add `tzdata` if you need to run in a different timezone. `tzdata` Is is already added to our base images.

```dockerfile
ARG BUILD_FROM
ARG BUILD_FROM=ghcr.io/home-assistant/base:latest
FROM $BUILD_FROM

# Install requirements for app
Expand All @@ -73,17 +72,15 @@ RUN chmod a+x /run.sh
CMD [ "/run.sh" ]
```

If you don't use local build on the device or our build script, make sure that the Dockerfile also has a set of labels that include:
If you are not using Home Assistant GitHub builder actions (see [Publishing your app](/docs/apps/publishing)), make sure that the Dockerfile also has a set of labels that include:

```dockerfile
LABEL \
io.hass.version="VERSION" \
io.hass.type="addon" \
io.hass.arch="armhf|aarch64|i386|amd64"
io.hass.type="app" \
io.hass.arch="aarch64|amd64"
```

It is possible to use your own base image with `build.yaml` or if you do not need support for automatic multi-arch building you can also use a simple docker `FROM`. You can also suffix the Dockerfile with the specific architecture to use a specific Dockerfile for a particular architecture, i.e. `Dockerfile.amd64`.

### Build args

We support the following build arguments by default:
Expand Down Expand Up @@ -116,11 +113,11 @@ map:
- type: homeassistant_config
read_only: False
path: /custom/config/path
image: repo/{arch}-my-custom-addon
image: ghcr.io/my-org/my-app
```

:::note
Avoid using `config.yaml` as filename in your app for anything other than the app configuration. The Supervisor does a recursively search for `config.yaml` in the app repository.
Avoid using `config.yaml` as filename in your app for anything other than the app configuration. The Supervisor does recursively search for `config.yaml` in the app repository.
:::

### Required configuration options
Expand Down Expand Up @@ -172,8 +169,7 @@ Avoid using `config.yaml` as filename in your app for anything other than the ap
| `legacy` | bool | `false` | If the Docker image has no `hass.io` labels, you can enable the legacy mode to use the config data.
| `options` | dict | | Default options value of the app.
| `schema` | dict | | Schema for options value of the app. It can be `false` to disable schema validation and options.
| `image` | string | | For use with Docker Hub and other container registries. This should be set to the name of the image only (E.g, `ghcr.io/home-assistant/{arch}-addon-example`). If you use this option, set the active docker tag using the `version` option.
| `codenotary` | string | | For use with Codenotary CAS. This is the E-Mail address used to verify your image with Codenotary (E.g, `example@home-assistant.io`). This should match the E-Mail address used as the signer in the [app's extended build options](#app-extended-build)
| `image` | string | | For use with container registries. Set this to the generic (multi-arch) image name, e.g. `ghcr.io/my-org/my-app`. The `{arch}` placeholder is still supported as a compatibility fallback for per-architecture image names (e.g. `ghcr.io/my-org/{arch}-my-app`). If you use this option, set the active Docker tag using the `version` option.
| `timeout` | integer | 10 | Default 10 (seconds). The timeout to wait until the Docker daemon is done or will be killed.
| `tmpfs` | bool | `false` | If this is set to `true`, the containers `/tmp` uses tmpfs, a memory file system.
| `discovery` | list | | A list of services that this app provides for Home Assistant.
Expand Down Expand Up @@ -269,30 +265,11 @@ We support:
- `list(val1|val2|...)`
- `device` / `device(filter)`: Device filter can be in the following format: `subsystem=TYPE` i.e. `subsystem=tty` for serial devices.

## App extended build

Additional build options for an app are stored in `build.yaml`. This file will be read from our build systems.
This is only needed if you are not using the default images or need additional things.

```yaml
build_from:
armhf: mycustom/base-image:latest
args:
my_build_arg: xy
```

| Key | Required | Description |
| --- | -------- | ----------- |
| build_from | no | A dictionary with the hardware architecture as the key and the base Docker image as the value.
| args | no | Allow additional Docker build arguments as a dictionary.
| labels | no | Allow additional Docker labels as a dictionary.
| codenotary | no | Enable container signature with codenotary CAS.
| codenotary.signer | no | Owner signer E-Mail address for this image.
| codenotary.base_image | no | Verify the base container image. If you use our official images, use `notary@home-assistant.io`
:::note

We provide a set of [base images][docker-base] which should cover a lot of needs. If you don't want to use the Alpine based version or need a specific image tag, feel free to pin this requirement for your build with the `build_from` option.
Previously, additional build options such as `build_from`, `args`, and `labels` were configured in a separate `build.yaml` file that was read by the legacy builder. This file is no longer used. Base images should be set directly with a `FROM` statement in your `Dockerfile`, labels with a `LABEL` statement, and custom build arguments with `ARG` definitions. See the [builder migration blog post](/blog/2026/04/02/builder-migration) for detailed migration instructions.

[docker-base]: https://github.com/home-assistant/docker-base
:::

## App translations

Expand Down
68 changes: 25 additions & 43 deletions docs/apps/publishing.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,65 +4,47 @@ title: "Publishing your app"

There are two different ways of publishing apps (formerly known as add-ons). One is to publish pre-built containers to a container registry and the other option is to have users build the containers locally on their Home Assistant instance.

#### Pre-built containers
## Pre-built containers

With pre-built containers, the developer is responsible for building the images for each architecture on their machine and pushing the results out to a container registry. This has a lot of advantages for the user who will only have to download the final container and be up and running once the download finishes. This makes the installation process fast and has almost no chance of failure so it is the preferred method.
With pre-built containers, the developer is responsible for building the images for each architecture and pushing the results to a container registry. This has a lot of advantages for the user who will only have to download the final container and be up and running once the download finishes. This makes the installation process fast and has almost no chance of failure, so it is the preferred method.

We have automated the process of building and publishing apps. See below for the instructions.
We have automated the process of building and publishing apps via GitHub Actions. See below for the instructions.

#### Locally build containers
## Locally built containers

With the Supervisor, it is possible to distribute apps that will be built on the users machine. The advantage is that as a developer it is easy to test an idea and see if people are interested in your apps. This method includes installing and potentially compiling code. This means that installing such an app is slow and adds more wear and tear to users SD card/hard drive than the above mentioned pre-built solution. It also has a higher chance of failure if one of the dependencies of the container has changed or is no longer available.
With the Supervisor, it is possible to distribute apps that will be built on the user's machine. The advantage is that for you as a developer it is easy to test an idea and see if people are interested in your apps. This method includes installing and potentially compiling code. This means that installing such an app is slow and adds more wear and tear to users' SD cards/hard drives than the above-mentioned pre-built solution. It also has a higher chance of failure if one of the dependencies of the container has changed or is no longer available.

Use this option when you are playing with apps and seeing if someone is interested in your work. Once you're an established repository, please migrate to pushing builds to a container registry as it greatly improves the user experience. In the future we will mark locally built apps in the app store to warn users.

## Build scripts to publish apps to a container registry
## Publishing apps to a container registry with GitHub Actions

All apps (formerly known as add-ons) are containers. Inside your app `config.yaml` you specify the container image that will be installed for your app:
The recommended way to build and publish multi-architecture app images is through GitHub Actions using the builder composite actions maintained by the Home Assistant project. These actions can be then used for a matrix of builds in an app repository. See the [builder workflow](https://github.com/home-assistant/apps-example/blob/main/.github/workflows/builder.yaml) in the example app repository for a complex example of building multiple apps, or the [example workflow](https://github.com/home-assistant/builder?tab=readme-ov-file#example-workflow) in the builder repository for a single app build. The builder actions are designed to be flexible and can be used in more complex workflows as needed.

### Image naming

Inside your app `config.yaml`, set the `image` field to the image name published in the container registry:

```yaml
...
image: "myhub/image-{arch}-addon-name"
...
image: "ghcr.io/my-org/my-app"
```

You can use `{arch}` inside the image name to support multiple architectures with one (1) configuration file. It will be replaced with the architecture of the user when we load the image. If you use `Buildargs` you can use the `build.yaml` to overwrite our default args.
The `{arch}` placeholder is still supported for backwards compatibility with per-architecture image publishing. When a multi-arch manifest is available, the generic name is the preferred public reference:

Home Assistant assumes that the default branch of your app repository matches the latest tag on the container registry. When you're building a new version, it's suggested that you use another branch, ie `build` or do it with a PR on GitHub. After you push the app to a container registry, you can merge this branch to master.

## Custom apps
```yaml
# Preferred — resolves via the multi-arch manifest
image: "ghcr.io/my-org/my-app"

You need a Docker Hub account to make your own apps. You can build your container images with the Docker `build` command or use our [builder] to simplify the process. Pull our [Builder Docker engine][builder] and run one of the following commands.
# Compatibility fallback — still works if only arch-prefixed images exist
image: "ghcr.io/my-org/{arch}-my-app"
```

For a git repository:
### Published images

```shell
docker run \
--rm \
--privileged \
-v ~/.docker/config.json:/root/.docker/config.json:ro \
ghcr.io/home-assistant/amd64-builder \
--all \
-t addon-folder \
-r https://github.com/xy/addons \
-b branchname
```
After a successful run, two types of image references are available:

For a local repository:

```shell
docker run \
--rm \
--privileged \
-v ~/.docker/config.json:/root/.docker/config.json:ro \
-v /my_addon:/data \
ghcr.io/home-assistant/amd64-builder \
--all \
-t /data
```
- **Per-architecture images** (e.g. `ghcr.io/my-org/aarch64-my-app:1.0.0`) — pushed by the `build-image` action.
- **Generic manifest image** (e.g. `ghcr.io/my-org/my-app:1.0.0`) — pushed by the `publish-multi-arch-manifest` action; this is the preferred reference to use in `config.yaml` and to share with users.

:::tip
If you are developing on macOS and using Docker for Mac, you may encounter an error message similar to the following: `error creating aufs mount to /var/lib/docker/aufs/mnt/<SOME_ID>-init: invalid argument`. A proposed workaround is to add the following to the Advanced Daemon JSON configuration via Docker > Preferences > Daemon > Advanced: `"storage-driver" : "aufs"` or map the docker socket into container.
:::
### Example app repository

[builder]: https://github.com/home-assistant/builder
See the [Home Assistant example app repository](https://github.com/home-assistant/apps-example) for a complete, up-to-date working example.
Loading