---
description: >-
  This section will guide through all of the steps involved with building, and
  hosting a multi-arch operator image.
---

# Building a Multi-Arch Operator Image

{% hint style="danger" %}
**Please note** that the following build process is **for demonstration purposes only** and is **NOT supported by Red Hat** due to the use of emulation software to enable cross-compilation and building.&#x20;

You should always build containers and application binaries on the native host platform that you intend to support. Emulation software should only be used to create non-production builds for testing.

It should also be noted that this procedure **will NOT work in RHEL** because the required emulation software is intentionally unavailable. Please see [https://access.redhat.com/solutions/5654221](https://access.redhat.com/solutions/5654221).
{% endhint %}

Here we showcase how to build an operator controller image targeting other (non-Intel/AMD) CPU architectures for hosting in an external registry. It also covers assembling the various architecture images into a single manifest list and tag. This section assumes that you've already made the required changes to the operator's Dockerfile (labels and licenses) when certifying for Intel/AMD.

{% hint style="warning" %}
Building an operand image for other CPU architectures is not covered in this guide. However, you can use the same `--arch` flag with [`podman`](https://podman.io) to force the use of a specific architecture, and the `GOARCH` variable would also still apply for container applications written in Go. &#x20;
{% endhint %}

If you already have a multi-arch operator and operands hosted in Quay or Docker Hub, please proceed with [Scanning and Publishing](scanning-and-publishing).

## Building with Legacy Operator SDK Releases (pre-1.0)

{% hint style="info" %}
If you don't have an Operator SDK project to work from, you can clone this git repository and follow along: [https://github.com/jsm84/rhm-env-example](https://github.com/jsm84/rhm-env-example)\
\
The operator project is located within the `ubi-noop-go/` directory of the git repo.
{% endhint %}

For older releases of the Operator SDK, where image building is handled by the `operator-sdk` binary, the process is fairly simple. There are no specific requirements to be made to the Dockerfile source for the operator controller. All modifications are made as flags or variables when calling `operator-sdk build`.

Specifically, you must set the `GOARCH` variable and `--arch` flag to match the CPU architecture that you're targeting. According to the CPU architecture table, the correct architecture (arch) for IBM Z would be `s390x`.

For cross compilation and container builds to work, you must install the `qemu-user-static` package, which is not available in RHEL (Fedora was used here):

```
$ sudo dnf install -y qemu-user-static
```

Let's build an image locally for the `s390x` architecture. Keep in mind that we're using [`podman`](https://podman.io) in docker emulation mode as the image build tool:

```
$ GOARCH=s390x operator-sdk build ubi-noop-go:v0.0.1-s390x --image-build-args="--arch=s390x"
```

Similarly, to build an operator image for IBM Power:

```
$ GOARCH=ppc64le operator-sdk build ubi-noop-go:v0.0.1-ppc64le --image-build-args="--arch=ppc64le"
```

OPTIONAL: If you _do not_ yet have an Intel/AMD operator image (a requirement for multi-arch operator certification) and want to follow along, then build the `amd64` image. Regardless, you must set the `arch` flag to `amd64` or `podman` will default to using the most recent image/architecture (ppc64le) for future commands:

```
$ operator-sdk build ubi-noop-go:v0.0.1-amd64 --image-build-args="--arch=amd64"
```

With all of the images built, the next step is to create a manifest list and push it to an external registry.

## Build and Push the Manifest List

In this section, we use `podman` exclusively for creating and pushing the manifest list. The syntax for `docker` is similar, with the exception of using a single argument when pushing the manifest to a registry.

Create a new manifest list, which will reference each of the underlying architecture-specific images:

```
$ podman manifest create ubi-noop-go:v0.0.1
```

{% hint style="info" %}
You could also populate the index by listing each sub-architecture image as an additional argument when creating the manifest list. However, if anything happens (typo/mispelling), then you're left with a partially populated manifest list.
{% endhint %}

Add each of the architecture specific images to the manifest list, in no particular order. To make sure that `podman` pulls the images from local container storage, we use the `containers-storage:localhost/` prefix. Let's start with the Intel/AMD image:

```
$ podman manifest add ubi-noop-go:v0.0.1 containers-storage:localhost/ubi-noop-go:v0.0.1-amd64
```

Next, add the Power image to the manifest list:

```
$ podman manifest add ubi-noop-go:v0.0.1 containers-storage:localhost/ubi-noop-go:v0.0.1-ppc64le
```

Add the Z image to complete the manifest list:

```
$ podman manifest add ubi-noop-go:v0.0.1 containers-storage:localhost/ubi-noop-go:v0.0.1-s390x
```

The last step is to push the new manifest list to an external registry for hosting. Quay.io is suggested here, but you can use any registry which supports manifest lists, such as Docker Hub:

```
$ podman manifest push ubi-noop-go:v0.0.1 quay.io/exampleco/ubi-noop-go:v0.0.1
```

Once you have the manifest list hosted for your operator _and_ its operands (which aren't covered here), you can proceed with [Scanning and Publishing](scanning-and-publishing).
