Installing OKD using Assisted Installer in a disconnected environment


Setting up OKD may become a challenge when you don’t have experience - this is where Assisted Installer may be helpful. In restricted networking environments setting up a mirror registry and configuring installer to use it instead of requires few additional steps.

This tutorial would cover the process of mirroring OKD release and steps to install disconnected Assisted Installer. On a helper node we’ll run container registry, copy OKD release images there and run Assisted Installer - a service which can guide us through creating a cluster which can only access the internal registry.

Note, that in my setup helper node with the registry has access both to and internal network (so called, “partially disconnected” installation). If your installation environment is fully air gapped, check out oc-mirror docs for mirror configuration options.

Our first step would be setting up container registry on a helper node.

Prepare certificates for registry

Use openssl to generate certificates or, simply, use LetsEncrypt wildcard cert:

mkdir /srv/registry/certs -p
cp -vf /etc/letsencrypt/live/ /srv/registry/certs/domain.crt
cp -vf /etc/letsencrypt/live/ /srv/registry/certs/domain.key

Generate authentication for local registry

Login: root Password: sikret

mkdir /srv/registry/auth -p
podman run --rm --entrypoint htpasswd -Bbn root sikret > /srv/registry/auth/htpasswd

Start a local registry

podman run -d \
  --restart=always \
  --name registry \
  -v /srv/registry/auth:/auth:z \
  -v /srv/registry/certs:/certs:z \
  -e REGISTRY_AUTH=htpasswd \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  -p 443:443 \
podman login

Host local source of Fedora CoreOS images

mkdir /srv/registry/fcos
cd /srv/registry/fcos
export FCOS_VERSION="36.20220806.3.0"
podman run -d \
  --name image-storage \
  -v /srv/registry/fcos:/data:z \
  -w /data \
  -p 3000:3000 \ python3 -m http.server 3000

Pull secret

Our registry requires authentication, so we’ll need to re-login to get a pullsecret:

mkdir ~/.docker
podman login --authfile=~/.docker/config.json
cat ~/.docker/config.json

Mirror OKD and additional images to the local registry

In order to run Assisted Installer we’re going to need to mirror some more additional images. oc-mirror is a great tool to keep those updated:

wget -O - | sudo tar -xz -C /usr/local/bin
wget -O - | sudo tar -xz -C /usr/local/bin
chmod a+x /usr/local/bin/oc-mirror

export OKD_VERSION="4.11.0-0.okd-2022-10-15-073651"

cat > /tmp/oc-mirror-config <<EOF
kind: ImageSetConfiguration
      - name: stable-4
        type: okd
        minVersion: "${OKD_VERSION}"
        maxVersion: "${OKD_VERSION}"
    - name:
    - name:
    - name:
    - name:
    - name:
    - name:
    - name:
    - name:
    - name:

oc-mirror can also make a custom mirrored OperatorHub:

cat >> /tmp/oc-mirror-config << EOF
    - catalog:
      headsOnly: false
        - name: argocd-operator
        - name: grafana-operator

See other oc-mirror features at the docs

Lets mirror this to our registry:

oc mirror --config /tmp/oc-mirror-config docker://

Run Assisted Installer in podman

Fetch configuration and pod definition:

mkdir /srv/assisted-service
cd /srv/assisted-service
wget -O disconnected-okd-configmap.yml
wget -O disconnected-pod.yml

These images are using and use as address. Instead we want to use the local mirror and

sed -i 's;;;g' disconnected-pod.yml
sed -i 's;;;g' disconnected-okd-configmap.yml
sed -i '/RELEASE_IMAGES/d' disconnected-okd-configmap.yml
sed -i '/OS_IMAGES/d' disconnected-okd-configmap.yml
sed -i '/OKD_RPMS_IMAGE/d' disconnected-okd-configmap.yml
cat >> disconnected-okd-configmap.yml << EOF

The cluster’s mirroring settings prevent mirroring by tags, so we need custom setting to use Assisted Installer controller image via digest

cat >> disconnected-okd-configmap.yml << EOF
  CONTROLLER_IMAGE: $(skopeo inspect docker:// --format "{{.Name}}@{{.Digest}}")

Once config and pod definition are updated for disconnected use, lets use podman to start Assisted Installer:

podman play kube --configmap disconnected-okd-configmap.yml disconnected-pod.yml

OKD installation

Now we can start the installation, let’s click “Create New Cluster” at

Cluster details

On “Operators” screen we’ll just click “Next” - no operators were mirrored:


At “Host discovery” stage usually, we’d generate the ISO and boot from it, but it would attempt to pull images from, so first, we need to patch these with mirroring configuration via Ignition override. This assisted installer option is available via API only, so we’ll use aicli to interact with it:

alias aicli='podman run --net host -it --rm -e -v /tmp:/workdir'

Before discovery ISO can be booted it needs to be amended with mirroring configuration:

cat > /tmp/install-override<<EOF
  - mirrors:
  - mirrors:
  - mirrors:
  - mirrors:

The installer also needs an SSH key to be able to ssh on the nodes:

cat >>/tmp/install-override<<EOF
ssh_public_key: |
    $(cat ~/.ssh/

Now we can patch the infrastructure environment, telling assisted image service to have these changes ending up in the cluster:

aicli update cluster disconnected --paramfile /workdir/install-override
aicli update infraenv disconnected --paramfile /workdir/install-override

where disconnected is the cluster name.

Click ‘Add Host’ to configure discovery ISO (ssh key is already filled in by aicli): Add Host dialog

“Generate Discovery ISO” button will show a link and suggested command to download discovery ISO: Discover ISO link

Alternatively, you can use aicli info iso disconnected to get ISO URL. aicli can work with the API the same way as UI (see docs), but in this tutorial, I’ll be using UI for the sake of demonstration.

Now we can boot hosts with discovery ISO.

Wait for all nodes to register themselves: The host has registered itself and click “Next”

This is a bare-metal installation, so on “Storage” screen we proceed by clicking “Next”.

Network details are autodetected from DHCP: Installation details

On the “Review and create” screen lets double-check all parameters: Installation details and start the installation.

After Assisted Installer verifies that nodes can pull all necessary images it would run the installer, generate bootstrap.ign, and pass it to the bootstrap node. The node would apply Ignition without reboot, run bootkube.service, and other bootstrap tasks. Now the node would run Machine Config Server so that other masters would be able to fetch master Ignition (if necessary), convert bootstrap node into master, and reboot. Install step 5 out of 10

The host would remain in “Rebooting” state until the assisted-installer controller would not be started as a pod and report the installation progress back to the Install step 8 out of 10

Now the host is installed and cluster installations proceeds. Once the ingress and console are running Assisted Installer UI would display the kubeadmin password and a link to the cluster console. Install step 9 out of 10

Once all operators have reported success the cluster installation is complete, and voila.


You can use your AcivityPub account to reply to this post.