{"id":50134773,"url":"https://github.com/rguske/openshift-agent-based-installer-airgapped","last_synced_at":"2026-05-23T21:03:43.715Z","repository":{"id":354216760,"uuid":"930796616","full_name":"rguske/openshift-agent-based-installer-airgapped","owner":"rguske","description":"Installing Red Hat OpenShift using the installation method Agent Based Installer in an air-gapped environment. ","archived":false,"fork":false,"pushed_at":"2026-05-04T21:00:42.000Z","size":3885,"stargazers_count":0,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-04T23:09:52.158Z","etag":null,"topics":["airgapped","disconnected","kubernetes","openshift","openshift-container-platform","redhat-openshift"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rguske.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-02-11T08:22:00.000Z","updated_at":"2026-05-04T21:00:47.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rguske/openshift-agent-based-installer-airgapped","commit_stats":null,"previous_names":["rguske/openshift-agent-based-installer-airgapped"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rguske/openshift-agent-based-installer-airgapped","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rguske%2Fopenshift-agent-based-installer-airgapped","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rguske%2Fopenshift-agent-based-installer-airgapped/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rguske%2Fopenshift-agent-based-installer-airgapped/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rguske%2Fopenshift-agent-based-installer-airgapped/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rguske","download_url":"https://codeload.github.com/rguske/openshift-agent-based-installer-airgapped/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rguske%2Fopenshift-agent-based-installer-airgapped/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33412082,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T18:09:33.147Z","status":"ssl_error","status_checked_at":"2026-05-23T18:09:31.380Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["airgapped","disconnected","kubernetes","openshift","openshift-container-platform","redhat-openshift"],"created_at":"2026-05-23T21:03:43.102Z","updated_at":"2026-05-23T21:03:43.698Z","avatar_url":"https://github.com/rguske.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Installing OpenShift in an air-gapped environment using the Agent-Based Installer\n\nA disconnected environment is an environment that does not have full access to the internet.\n\nOpenShift Container Platform is designed to perform many automatic functions that depend on an internet connection, such as retrieving release images from a registry or retrieving update paths and recommendations for the cluster. Without a direct internet connection, you must perform additional setup and configuration for your cluster to maintain full functionality in the disconnected environment.\n\nSource: [Understanding disconnected installation mirroring](https://docs.redhat.com/en/documentation/openshift_container_platform/4.21/html/installing_an_on-premise_cluster_with_the_agent-based_installer/understanding-disconnected-installation-mirroring)\n\n---\n- [Installing OpenShift in an air-gapped environment using the Agent-Based Installer](#installing-openshift-in-an-air-gapped-environment-using-the-agent-based-installer)\n  - [How it works](#how-it-works)\n  - [Connected Mirroring vs Disconnected Mirroring](#connected-mirroring-vs-disconnected-mirroring)\n  - [Bastion Host Preperation](#bastion-host-preperation)\n    - [Hostname](#hostname)\n    - [RHEL Subscription Manager](#rhel-subscription-manager)\n    - [Networking Bastion-Host](#networking-bastion-host)\n    - [SSH](#ssh)\n    - [Command Line Interfaces (CLIs)](#command-line-interfaces-clis)\n    - [Install Podman and Nmstate](#install-podman-and-nmstate)\n    - [Installing Podman Offline](#installing-podman-offline)\n  - [Installing the Mirror Registry on the Bastion Host](#installing-the-mirror-registry-on-the-bastion-host)\n    - [Prerequisites](#prerequisites)\n    - [Validating the installation](#validating-the-installation)\n    - [Login into the Mirror Registry](#login-into-the-mirror-registry)\n    - [Uninstalling the Mirror Registry](#uninstalling-the-mirror-registry)\n  - [Mirroring Images](#mirroring-images)\n  - [Creating the image set configuration](#creating-the-image-set-configuration)\n    - [unexpected status code 413 Request Entity Too Large](#unexpected-status-code-413-request-entity-too-large)\n  - [Installing a disconnected Cluster using the Agent Based Installer](#installing-a-disconnected-cluster-using-the-agent-based-installer)\n    - [Cluster Preperations](#cluster-preperations)\n  - [Configurations](#configurations)\n  - [Create Agent iso](#create-agent-iso)\n  - [Run a `httpd` webserver on the bastion to share the iso](#run-a-httpd-webserver-on-the-bastion-to-share-the-iso)\n  - [Using Operator Lifecycle Manager in disconnected environments](#using-operator-lifecycle-manager-in-disconnected-environments)\n  - [Troubleshooting](#troubleshooting)\n    - [Networking](#networking)\n    - [Logs](#logs)\n    - [Cluster Status validations](#cluster-status-validations)\n    - [Firewall is blocking images from pulling](#firewall-is-blocking-images-from-pulling)\n\n---\n\n## How it works\n\nYou can use a mirror registry for disconnected installations and to ensure that your clusters only use container images that satisfy your organization’s controls on external content. Before you install a cluster on infrastructure that you provision in a disconnected environment, you must mirror the required container images into that environment. To mirror container images, you must have a registry for mirroring.\n\n## Connected Mirroring vs Disconnected Mirroring\n\n**Connected Mirroring** is if you have a host that can access both the internet and your mirror registry, but not your cluster nodes, you can directly mirror the content from that machine.\n\n**Disconnected Mirroring** is if you have no such host, you must mirror the images to a file system and then bring that host or removable media into your restricted environment.\n\n## Bastion Host Preperation\n\n### Hostname\n\nMake sure that your `hostname` is set correctly!\n\n```code\nhostnamectl\n```\n\nThe hostname must be a fqdn!\n\n```code\nsudo hostnamectl set-hostname rguske-rhel9-disco-bastion.rguske.coe.muc.redhat.com\nsudo reboot\n```\n\n### RHEL Subscription Manager\n\nConfigure RHEL Subscription Manager:\n\n`sudo subscription-manager register --username --password '' --auto-attach`\n\n### Networking Bastion-Host\n\nSetup a Bastion Host with two nics. One is connected to the \"internet-zone\" and the other one to the disconnected network.\n\n![network-diagram](assets/openshift-mirror-registry-diagramm.png)\n\nConfigure the interfaces accordingly:\n\nConfigure the interface which has internet connection:\n\n```code\nnmcli con show\n\nNAME                UUID                                  TYPE      DEVICE\nSystem eth0         5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03  ethernet  eth0\nlo                  dd314177-6d3f-4ad3-a1af-2875d094c193  loopback  lo\nWired connection 1  c8d40ef7-3d02-3ba5-b047-dacd6d013b24  ethernet  --\n```\n\n```bash\nnmcli con mod \"System eth0\" \\\nipv4.addresses 10.32.96.145/20 \\\nipv4.gateway 10.32.111.254 \\\nipv4.dns \"10.32.96.1,10.32.96.31\" \\\nipv4.method manual\n```\n\nConfigure the interface which is connected to the disconnected network:\n\n```bash\nnmcli con mod \"Wired connection 1\" \\\nipv4.addresses 192.168.69.208/24 \\\nipv4.method manual\n```\n\nBring both interfaces up:\n\n```code\nnmcli dev reapply eth0 \u0026\u0026 nmcli dev reapply eth1\n\nConnection successfully reapplied to device 'eth0'.\nConnection successfully reapplied to device 'eth1'.\n```\n\nAlternatively:\n\n```code\nnmcli con up \"System eth0\" \u0026\u0026 nmcli con up \"Wired connection 1\"\n\nConnection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/25)\nConnection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/26)\n```\n\nCheck the new config:\n\n```code\nip -br a\nlo               UNKNOWN        127.0.0.1/8 ::1/128\neth0             UP             10.32.96.145/20 2620:52:0:2060:d8:6dff:fe0f:3ed3/64 fe80::d8:6dff:fe0f:3ed3/64\neth1             UP             192.168.69.208/24 fe80::a0eb:9896:d4e1:3fbb/64\n```\n\nThe configuration is stored under:\n\n```code\nnmcli -f NAME,UUID,FILENAME con show\nNAME                UUID                                  FILENAME\nSystem eth0         5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03  /etc/sysconfig/network-scripts/ifcfg-eth0\nWired connection 1  c8d40ef7-3d02-3ba5-b047-dacd6d013b24  /etc/NetworkManager/system-connections/Wired connection 1.nmconnection\nlo                  dd314177-6d3f-4ad3-a1af-2875d094c193  /run/NetworkManager/system-connections/lo.nmconnection\n```\n\nReadable configuration:\n\n```bash\nless /etc/sysconfig/network-scripts/ifcfg-eth0\n\n# Created by cloud-init automatically, do not edit.\n#\nAUTOCONNECT_PRIORITY=120\nBOOTPROTO=none\nDEVICE=eth0\nHWADDR=02:D8:6D:0F:3E:D3\nIPV6INIT=yes\nONBOOT=yes\nTYPE=Ethernet\nUSERCTL=no\nPROXY_METHOD=none\nBROWSER_ONLY=no\nIPADDR=10.32.96.145\nPREFIX=20\nGATEWAY=10.32.111.254\nDNS1=10.32.96.1\nDNS2=10.32.96.31\nDEFROUTE=yes\nIPV4_FAILURE_FATAL=no\nIPV6_AUTOCONF=yes\nIPV6_DEFROUTE=yes\nIPV6_FAILURE_FATAL=no\nNAME=\"System eth0\"\nUUID=5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03\n```\n\nAnd the other interface:\n\n```bash\nless /etc/NetworkManager/system-connections/Wired\\ connection\\ 1.nmconnection\n\n[connection]\nid=Wired connection 1\nuuid=c8d40ef7-3d02-3ba5-b047-dacd6d013b24\ntype=ethernet\nautoconnect-priority=-999\ninterface-name=eth1\n\n[ethernet]\n\n[ipv4]\naddress1=192.168.69.208/24\nmethod=manual\n\n[ipv6]\naddr-gen-mode=default\nmethod=auto\n\n[proxy]\n```\n\nEnable IP Forwarding: `sysctl -w net.ipv4.ip_forward=1`\n\nThis allows the VM to forward packets between interfaces.\n\nMake it permanently:\n\n```bash\nvi /etc/sysctl.conf\nnet.ipv4.ip_forward = 1\n```\n\nApply the changes: `sysctl -p`\n\nOn Red Hat Enterprise Linux, `firewall-cmd` is provided by the firewalld package. It is not guaranteed to be installed in minimal VM images.\n\nCheck whether firewalld is installed:\n\n```code\nrpm -q firewalld\n```\n\nIf it is missing, install and enable it:\n\n```code\nsudo dnf install -y firewalld\nsudo systemctl enable --now firewalld\n```\n\nAllow forwarding:\n\n`firewall-cmd --permanent --add-forward-port=port=22:proto=tcp:toport=22`\n\nAdd trusted zones for both interfaces:\n\n```bash\nfirewall-cmd --permanent --zone=public --add-interface=eth0\nfirewall-cmd --permanent --zone=trusted --add-interface=eth1\n```\n\nCheck the configs:\n\n```code\nfirewall-cmd --get-active-zones\npublic\n  interfaces: eth0\ntrusted\n  interfaces: eth1\n```\n\nAlso:\n\n```code\nfirewall-cmd --zone=public --list-all\npublic (active)\n  target: default\n  icmp-block-inversion: no\n  interfaces: eth0\n  sources:\n  services: cockpit dhcpv6-client ssh\n  ports:\n  protocols:\n  forward: yes\n  masquerade: no\n  forward-ports:\n  source-ports:\n  icmp-blocks:\n  rich rules:\n```\n\n```code\nfirewall-cmd --zone=trusted --list-all\ntrusted (active)\n  target: ACCEPT\n  icmp-block-inversion: no\n  interfaces: eth1\n  sources:\n  services:\n  ports:\n  protocols:\n  forward: yes\n  masquerade: no\n  forward-ports:\n  source-ports:\n  icmp-blocks:\n  rich rules:\n```\n\nWe will need to open up port 8443 for Quay:\n\n```code\nfirewall-cmd --add-port 8443/tcp --permanent\n```\n\nReload firewall rules: `firewall-cmd --reload`\n\nConfigure Routing (if needed).\n\nIf devices in 192.168.69.0/24 need internet access via the VM, you need NAT:\n\n```bash\nsudo firewall-cmd --permanent --add-masquerade\nsudo firewall-cmd --reload\n```\n\nCheck IP forwarding: `cat /proc/sys/net/ipv4/ip_forward`\n\n### SSH\n\nConfigure `ssh`:\n\n`cat ~/.ssh/id_ed25519.pub | ssh rguske@rguske-rhel9-disco-bastion.rguske.coe.muc.redhat.com \"mkdir -p ~/.ssh \u0026\u0026 cat \u003e\u003e ~/.ssh/authorized_keys \u0026\u0026 chmod 600 ~/.ssh/authorized_keys \u0026\u0026 chmod 700 ~/.ssh\"`\n\n- Generating an SSH key pair on your Bastion-Host. You can use this key pair to authenticate into the OpenShift Container Platform cluster’s nodes after it is deployed.\n\n`ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519`\n\n### Command Line Interfaces (CLIs)\n\nOn the bastion host, download the necessary cli's from [Homepage](https://console.redhat.com/openshift/downloads):\n\nYou could use `curl -LO \u003curl\u003e` for it:\n\nOpenShift Installer: `curl -LO https://mirror.openshift.com/pub/openshift-v4/clients/ocp/4.21.11/openshift-install-rhel9-amd64.tar.gz`\n\nOpenShift Client: `curl -LO https://mirror.openshift.com/pub/openshift-v4/clients/ocp/4.21.11/openshift-client-linux-amd64-rhel9-4.21.11.tar.gz`\n\nThe \"mirror\" plugin for the OpenShift CLI client (oc) controls the process of mirroring all relevant container image for a full disconnected OpenShift installation in a central, declarative tool. Learn more(new window or tab)\n\nRHEL 9 is FIPS compatible; RHEL 8 is non-FIPS compatible.\n\nOpenShift Mirror CLI: `curl -LO https://mirror.openshift.com/pub/openshift-v4/x86_64/clients/ocp/latest/oc-mirror.rhel9.tar.gz`\n\nDownload and install a local, minimal single instance deployment of Red Hat Quay to aid bootstrapping the first disconnected cluster.\n\nOpenShift Tiny Quay Registry: `curl -LO https://mirror.openshift.com/pub/cgw/mirror-registry/latest/mirror-registry-amd64.tar.gz`\n\nThis helps untaring the packages:\n\n```code\nalias untar='tar -zxvf'\n```\n\nThis is how it should like if you've downloaded all:\n\n```code\ntree\n.\n├── clis\n│   ├── oc-mirror.rhel9.tar.gz\n│   ├── openshift-client-linux-amd64-rhel9-4.21.11.tar.gz\n│   └── openshift-install-rhel9-amd64.tar.gz\n└── mirror-registry\n    └── mirror-registry-amd64.tar.gz\n```\n\nUnpack the `.gz` files, except execution-environment.tar, image-archive.tar and sqlite3.tar of the folder mirror-registry and move them into `/usr/local/bin`:\n\n```code\nsudp mv {kubectl,oc,oc-mirror,openshift-install-fips} /use/local/bin\n```\n\nApply rights:\n\n```code\nsudo chown -R $USER /usr/local/bin/{kubectl,oc,oc-mirror,openshift-install-fips}\nsudo chmod +x /usr/local/bin/{kubectl,oc,oc-mirror,openshift-install-fips}\n```\n\n```code\ntree /usr/local/bin\n/usr/local/bin\n├── execution-environment.tar\n├── firstboot-network-firewall.sh\n├── image-archive.tar\n├── kubectl\n├── mirror-registry\n├── oc\n├── oc-mirror\n├── openshift-install-fips\n└── sqlite3.tar\n\n0 directories, 9 file\n```\n\nIf /usr/local/bin isn't included in the `$PATH`, run\n`export PATH=/usr/local/bin:$PATH`\n\n### Install Podman and Nmstate\n\nIn order to run the mirror registry, the bastion host needs a container-runtime installed.\n\nPodman is the runtime of choice and is included in the `container-tools` package.\n\nInstall it via `sudo dnf install container-tools -y`.\n\nThe installer also uses `nmstatectl` for the creation of the agent.iso. Install it via `sudo dnf install nmstate -y`. Otherwise, you'll get the error:\n\n```code\nFATAL   * failed to validate network yaml for host 0, install nmstate package, exec: \"nmstatectl\": executable file not found in $PATH\n```\n\n### Installing Podman Offline\n\n1. Mount a RHEL installation iso file to the system (VM or BM via Board Management Controller).\n\nCheck e.g. with `lsblk` for the disconnected \"cdrom\" (iso) device:\n\n```bash\n[root@mirror-rguske ~]# lsblk\nNAME          MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS\nsda             8:0    0   120G  0 disk\n├─sda1          8:1    0   600M  0 part /boot/efi\n├─sda2          8:2    0     1G  0 part /boot\n└─sda3          8:3    0 118.4G  0 part\n  ├─rhel-root 253:0    0    70G  0 lvm  /\n  ├─rhel-swap 253:1    0   7.9G  0 lvm  [SWAP]\n  └─rhel-home 253:2    0  40.5G  0 lvm  /home\nsr0            11:0    1    11G  0 rom\n```\n\n2. Create a folder in which the iso content will be accessible:\n\n`mkdir -p /mnt/rhel-iso`\n\n3. Mount the connected iso accordingly:\n\n`mount -o loop /path/to/rhel.iso /mnt/rhel-iso`\n\nExample in my case with a VM: `mount -o loop /dev/sr0 /mnt/rhel-iso`\n\n4. Create a Local Repository:\n\n```code\ncat \u003c\u003cEOF | tee /etc/yum.repos.d/rhel9-iso.repo\n[rhel9-iso-BaseOS]\nname=RHEL9-ISO-BaseOS\nbaseurl=file:///mnt/rhel-iso/BaseOS/\nenabled=1\ngpgcheck=0\n\n[rhel9-iso-AppStream]\nname=RHEL9-ISO-AppStream\nbaseurl=file:///mnt/rhel-iso/AppStream/\nenabled=1\ngpgcheck=0\nEOF\n```\n\n5. Install Podman and `nmstate`\n\n`dnf install podman -y`\n`dnf install nmstate -y`\n\n## Installing the Mirror Registry on the Bastion Host\n\n### Prerequisites\n\n- An OpenShift Container Platform subscription.\n- Red Hat Enterprise Linux (RHEL) 8 and 9 with Podman 3.4.2 or later and OpenSSL installed. If you are using Podman 5.7 or later, see \"Configuring rootless Podman networking\".\n- Fully qualified domain name for the Red Hat Quay service, which must resolve through a DNS server.\n- Key-based SSH connectivity on the target host. SSH keys are automatically generated for local installs. For remote hosts, you must generate your own SSH keys.\n- 2 or more vCPUs.\n- 8 GB of RAM.\n- About 12 GB for OpenShift Container Platform 4.21 release images, or about 358 GB for OpenShift Container Platform 4.21 release images and OpenShift Container Platform 4.21 Red Hat Operator images.\n\nYou can use any container registry that supports Docker v2-2, such as Red Hat Quay, the mirror registry for Red Hat OpenShift, Artifactory, Sonatype Nexus Repository, or Harbor.\n\u003e The OpenShift image registry cannot be used as the target registry because it does not support pushing without a tag, which is required during the mirroring process.\n\nInstall the mirror registry:\n\nAt this point it is important to understand that my bastion is dual-homed.\n\n```code\ndig +short rguske-rhel9-disco-bastion.rguske.coe.muc.redhat.com\n10.32.96.145\ndig +short rguske-rhel9-disco-bastion.disco.local\n192.168.69.208\n```\n\nIn this scenario it is important to use the DNS record which points to the IP in the disco subnet.\n\n```code\n[root@bastion-rguske mirror-registry]# mirror-registry install --quayHostname rguske-rhel9-disco-bastion.disco.local --quayRoot '/home/$USER/downloads/mirror-registry/root' --initPassword 'r3dh4t1!' --verbose\n\n   __   __\n  /  \\ /  \\     ______   _    _     __   __   __\n / /\\ / /\\ \\   /  __  \\ | |  | |   /  \\  \\ \\ / /\n/ /  / /  \\ \\  | |  | | | |  | |  / /\\ \\  \\   /\n\\ \\  \\ \\  / /  | |__| | | |__| | / ____ \\  | |\n \\ \\/ \\ \\/ /   \\_  ___/  \\____/ /_/    \\_\\ |_|\n  \\__/ \\__/      \\ \\__\n                  \\___\\ by Red Hat\n Build, Store, and Distribute your Containers\n\nINFO[2026-04-22 05:16:03] Install has begun\nDEBU[2026-04-22 05:16:03] Ansible Execution Environment Image: quay.io/quay/mirror-registry-ee:latest\nDEBU[2026-04-22 05:16:03] Pause Image: registry.access.redhat.com/ubi8/pause:8.10-5\nDEBU[2026-04-22 05:16:03] Quay Image: registry.redhat.io/quay/quay-rhel8:v3.12.14\nDEBU[2026-04-22 05:16:03] Redis Image: registry.redhat.io/rhel8/redis-6:1-1766406130\nINFO[2026-04-22 05:16:03] Found execution environment at /usr/local/bin/execution-environment.tar\nINFO[2026-04-22 05:16:03] Loading execution environment from execution-environment.tar\nDEBU[2026-04-22 05:16:03] Importing execution environment with command: /bin/bash -c /usr/bin/podman image import \\\n                                        --change 'ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' \\\n                                        --change 'ENV HOME=/home/runner' \\\n                                        --change 'ENV container=oci' \\\n                                        --change 'ENTRYPOINT=[\"entrypoint\"]' \\\n                                        --change 'WORKDIR=/runner' \\\n                                        --change 'EXPOSE=6379' \\\n                                        --change 'VOLUME=/runner' \\\n                                        --change 'CMD [\"ansible-runner\", \"run\", \"/runner\"]' \\\n                                        - quay.io/quay/mirror-registry-ee:latest \u003c /usr/local/bin/execution-environment.tar\nGetting image source signatures\nCopying blob 159de7f3f142 done   |\nCopying config 3055d6ebc0 done   |\nWriting manifest to image destination\nsha256:3055d6ebc0dd81d1b676e94a7c1c06eef2c94ddc5abe2f197147b013184afd81\nINFO[2026-04-22 05:16:13] Detected an installation to localhost\nINFO[2026-04-22 05:16:13] Did not find SSH key in default location. Attempting to set up SSH keys.\nINFO[2026-04-22 05:16:13] Generating SSH Key\nGenerating public/private rsa key pair.\nYour identification has been saved in /root/.ssh/quay_installer\nYour public key has been saved in /root/.ssh/quay_installer.pub\n\n[...]\n\nTASK [mirror_appliance : Create init user] ***********************************************************************************************************************************************************************************************************************************************************************\nincluded: /runner/project/roles/mirror_appliance/tasks/create-init-user.yaml for rguske@rguske-rhel9-disco-bastion.rguske.coe.muc.redhat.com\n\nTASK [mirror_appliance : Creating init user at endpoint https://rguske-rhel9-disco-bastion.disco.local:8443/api/v1/user/initialize] ******************************************************************************************************************************************************************************\nok: [rguske@rguske-rhel9-disco-bastion.rguske.coe.muc.redhat.com]\n\nTASK [mirror_appliance : Enable lingering for systemd user processes] ********************************************************************************************************************************************************************************************************************************************\nchanged: [rguske@rguske-rhel9-disco-bastion.rguske.coe.muc.redhat.com]\n\nPLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************************************************\nrguske@rguske-rhel9-disco-bastion.rguske.coe.muc.redhat.com : ok=49   changed=29   unreachable=0    failed=0    skipped=15   rescued=0    ignored=0\n\nINFO[2026-04-24 10:18:34] Quay installed successfully, config data is stored in /home/$USER/downloads/mirror-registry/root\nINFO[2026-04-24 10:18:34] Quay is available at https://rguske-rhel9-disco-bastion.disco.local:8443 with credentials (init, r3dh4t1!)\n```\n\nAlternative customizations examples:\n\n1.: `/mirror-registry install --quayHostname mirror.example.org --sslKey tls.key --targetHostname internal.mirror --quayRoot /var/mirror-registry --initPassword changeme`\n\nSource: [Mirror registry for Red Hat OpenShift flags](https://docs.redhat.com/en/documentation/openshift_container_platform/4.21/html/disconnected_environments/installing-mirroring-creating-registry).\n\n### Validating the installation\n\nValidating the endpoint using `curl`:\n\n```code\ncurl -k https://rguske-rhel9-disco-bastion.disco.local:8443/health/instance\n{\"data\":{\"services\":{\"auth\":true,\"database\":true,\"disk_space\":true,\"registry_gunicorn\":true,\"service_key\":true,\"web_gunicorn\":true}},\"status_code\":200}\n```\n\nChecking the certificate:\n\n```code\necho | openssl s_client -connect rguske-rhel9-disco-bastion.disco.local:8443 -showcerts\nConnecting to 192.168.69.208\nCONNECTED(00000003)\ndepth=1 C=US, ST=VA, L=New York, O=Quay, OU=Division, CN=rguske-rhel9-disco-bastion.disco.local\nverify error:num=19:self-signed certificate in certificate chain\nverify return:1\ndepth=1 C=US, ST=VA, L=New York, O=Quay, OU=Division, CN=rguske-rhel9-disco-bastion.disco.local\nverify return:1\ndepth=0 CN=quay-enterprise\nverify return:1\n---\nCertificate chain\n 0 s:CN=quay-enterprise\n   i:C=US, ST=VA, L=New York, O=Quay, OU=Division, CN=rguske-rhel9-disco-bastion.disco.local\n   a:PKEY: RSA, 2048 (bit); sigalg: sha256WithRSAEncryption\n   v:NotBefore: Apr 24 14:17:18 2026 GMT; NotAfter: Apr 15 14:17:18 2027 GMT\n-----BEGIN CERTIFICATE-----\n\n[...]\n```\n\nAlso, validate the certificate which we are going trust on our bastion host.\n\n```code\nopenssl x509 -in ~/downloads/mirror-registry/root/quay-config/ssl.cert -text -noout\nCertificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number:\n            16:06:dd:b0:be:99:47:81:76:52:85:9c:15:1e:76:0d:ab:99:35:ea\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: C=US, ST=VA, L=New York, O=Quay, OU=Division, CN=rguske-rhel9-disco-bastion.disco.local\n        Validity\n            Not Before: Apr 24 14:17:18 2026 GMT\n            Not After : Apr 15 14:17:18 2027 GMT\n[...]\n```\n\nSystemd auto-start is also configured:\n\n```code\nsudo systemctl list-units --type service | grep quay\n  quay-app.service                                      loaded active running Quay Container\n  quay-pod.service                                      loaded active exited  Infra Container for Quay\n  quay-redis.service                                    loaded active running Redis Podman Container for Quay\n```\n\n### Login into the Mirror Registry\n\n```code\npodman login -u init -p 'r3dh4t1!' https://rguske-rhel9-disco-bastion.disco.local:8443 --tls-verify=false\n```\n\nIt is also possible without using the option `--tls-verify=false` by trusting the newly created certificates which are stored in `root` / `quay-config`:\n\n```code\ntree\n.\n├── pause.tar\n├── quay.tar\n├── redis.tar\n└── root\n    ├── quay-config\n    │   ├── config.yaml\n    │   ├── openssl.cnf\n    │   ├── ssl.cert\n    │   ├── ssl.csr\n    │   └── ssl.key\n    └── quay-rootCA\n        ├── rootCA.key\n        ├── rootCA.pem\n        └── rootCA.srl\n```\n\nCopy the certs:\n\n`sudo cp ~/downloads/mirror-registry/root/quay-config/ssl.cert /etc/pki/ca-trust/source/anchors/`\n\n```code\ntree /etc/pki/ca-trust/source/anchors/\n└── ssl.cert\n\n0 directories, 1 file\n```\n\nUpdate the trust:\n\n`update-ca-trust`\n\nLogout:\n\n```code\npodman logout https://rguske-rhel9-disco-bastion.disco.local:8443\n\nRemoved login credentials for rguske-rhel9-disco-bastion.disco.local:8443\n```\n\nLogin again:\n\n```code\npodman login -u init -p 'r3dh4t1!' 'https://rguske-rhel9-disco-bastion.disco.local:8443'\n\nLogin Succeeded!\n```\n\n### Uninstalling the Mirror Registry\n\n`mirror-registry uninstall`\n\n## Mirroring Images\n\n\u003e You must have access to the internet to obtain the necessary container images. In this procedure, you place your mirror registry on a mirror host that has access to both your network and the internet. If you do not have access to a mirror host, use the [Mirroring Operator catalogs](https://docs.redhat.com/en/documentation/openshift_container_platform/4.17/html-single/disconnected_environments/index#olm-mirror-catalog_installing-mirroring-installation-images) for use with disconnected clusters procedure to copy images to a device you can move across network boundaries with.\n\nProcedure and prerequisites:\n\n- You configured a mirror registry to use in your disconnected environment.\n- You identified an image repository location on your mirror registry to mirror images into.\n- You provisioned a mirror registry account that allows images to be uploaded to that image repository.\n- You have write access to the mirror registry.\n\nObtain your [pull secret from Red Hat OpenShift Cluster Manager](https://console.redhat.com/openshift/install/pull-secret) and paste the json content into $XDG_RUNTIME_DIR/containers/auth.json. Be careful! It must be in `json`.\n\nMake a copy of your pull secret in JSON format by running the following command:\n\nPaste the content in the file pull-scret. Then:\n\n`cat ./pull-secret | jq . \u003e $(pwd)/pull-secret.json`\n\nReplace the existing `auth.json` file in $XDG_RUNTIME_DIR/containers/\n\n```code\nsudo mv pull-secret.json $XDG_RUNTIME_DIR/containers/auth.json\n```\n\nNext up is to generate the base64-encoded user name and password or token for your mirror registry by running the following command:\n\n`echo -n '\u003cuser_name\u003e:\u003cpassword\u003e' | base64 -w0`\n\nFor \u003cuser_name\u003e and \u003cpassword\u003e, specify the user name and password that you configured for your registry.\n\nExample:\n\n`echo -n 'init:r3dh4t1!' | base64 -w0`\n\nEdit the JSON file and add a section that describes your registry to it:\n\n```json\n  \"auths\": {\n    \"rguske-rhel9-disco-bastion.rguske.coe.muc.redhat.com:8443\": {\n      \"auth\": \"aW5pdDpyM2RoNHQxIQ==\",\n      \"email\": \"rguske@redhat.com\"\n    },\n    \"cloud.openshift.com\": {\n\n[...]\n```\n\n## Creating the image set configuration\n\nCreate an `ImageSetConfiguration` YAML file and modify it to include your required images.\n\nList the available Operator using e.g.:\n\n```code\noc mirror list operators --catalogs --version=4.21 --v1\n```\n\n```yaml\ntee imagesetconfiguration.yaml \u003e /dev/null \u003c\u003c'EOF'\nkind: ImageSetConfiguration\napiVersion: mirror.openshift.io/v2alpha1\nmirror:\n  platform:\n    channels:\n    - name: stable-4.21\n      type: ocp\n      shortestPath: true\n      minVersion: 4.21.10\n      maxVersion: 4.21.11\n    graph: true\n  operators:\n  - catalog: registry.redhat.io/redhat/redhat-operator-index:v4.21\n    packages:\n## cincinnati-operator:v1\n    - name: cincinnati-operator\n      channels:\n      - name: v1\n        minVersion: '5.0.3'\n        # maxVersion: '5.0.3'\n## kubernetes-nmstate-operator:stable\n    - name: kubernetes-nmstate-operator\n      channels:\n      - name: 'stable'\n        minVersion: '4.21.0-202604080925'\n        # maxVersion: '4.17.0-202502120148'\n## kubevirt-hyperconverged:stable\n    - name: kubevirt-hyperconverged\n      channels:\n      - name: stable\n        minVersion: '4.21.3'\n        # maxVersion: '4.17.4'\n## metallb-operator:stable\n    - name: metallb-operator\n      channels:\n      - name: stable\n        minVersion: '4.21.0-202604140043'\n        # maxVersion: 'v4.17.0'\n## web-terminal:fast\n    - name: web-terminal\n      channels:\n      - name: fast\n        minVersion: '1.16.0'\n        # maxVersion: 'v1.15.0'\n## web-terminal relies on devworkspace-operator\n#    - name: devworkspace-operator\n#      channels:\n#      - name: fast\n#        minVersion: '0.40-1776457293'\n## OpenShift Data Foundation\n    - name: odf-operator\n      channels:\n      - name: stable-4.21\n        minVersion: '4.21.2-rhodf'\n## OpenShift Local Storage Operator\n    - name: local-storage-operator\n      channels:\n      - name: stable\n        minVersion: '4.21.0-202604200440'\n  additionalImages:\n    - name: registry.redhat.io/ubi8/ubi:latest\n    - name: registry.redhat.io/rhel9/rhel-guest-image:latest\n    - name: quay.io/rhn_support_sreber/curl:latest\n    # Important for KMM \u0026 GPFS Build\n    - name: registry.redhat.io/ubi9/ubi-minimal:latest\n    - name: registry.redhat.io/ubi9/ubi@sha256:20f695d2a91352d4eaa25107535126727b5945bff38ed36a3e59590f495046f0\n    - name: quay.io/rguske/vddk@sha256:26d07e11f7f8dcca263e83a1d942fe9274c90418c5bfc17fad88b61ddabf95ed\n    - name: quay.io/rguske/simple-web-app@sha256:f1c474d0b214975d2fb95d14967b620daa0cdbef094ee509fec1659d55c3a6de\n    # Virtualization Images\n    - name: quay.io/containerdisks/centos-stream:9\n    - name: quay.io/containerdisks/fedora:latest\nEOF\n```\n\nEnsure that clis are in your $PATH. Otherwise `export PATH=/usr/local/bin:$PATH`.\n\nThe oc-mirror plugin v2 automatically generates the following custom resources:\n\n- `ImageDigestMirrorSet` (IDMS)\nHandles registry mirror rules when using image digest pull specifications. Generated if at least one image of the image set is mirrored by digest.\n- `ImageTagMirrorSet` (ITMS)\nHandles registry mirror rules when using image tag pull specifications. Generated if at least one image from the image set is mirrored by tag.\n- `CatalogSource`\nRetrieves information about the available Operators in the mirror registry. Used by Operator Lifecycle Manager (OLM) Classic.\n- `ClusterCatalog`\nRetrieves information about the available cluster extensions (which includes Operators) in the mirror registry. Used by OLM v1.\n- `UpdateService`\nProvides update graph data to the disconnected environment. Used by the OpenShift Update Service.\n\nMirror the images from the specified image set configuration to the disk by running the following command:\n\n```code\noc mirror -c $(pwd)/openshift/imagesetconfiguration.yaml file://$(pwd) --v2\n```\n\n```code\noc-mirror -c $(pwd)/openshift/imagesetconfiguration.yaml file:///home/rguske/openshift/mirror --v2\n```\n\nResult:\n\n```code\n2026/04/23 12:55:55  [INFO]   : === Results ===\n2026/04/23 12:55:55  [INFO]   :  ✓  193 / 193 release images mirrored successfully\n2026/04/23 12:55:55  [INFO]   :  ✓  95 / 95 operator images mirrored successfully\n2026/04/23 12:55:55  [INFO]   :  ✓  7 / 7 additional images mirrored successfully\n2026/04/23 12:55:55  [INFO]   : 📦 Preparing the tarball archive...\n2026/04/23 12:58:48  [INFO]   : mirror time     : 19m53.229903012s\n2026/04/23 12:58:48  [INFO]   : 👋 Goodbye, thank you for using oc-mirror\n```\n\n```code\nls -ltr\ntotal 61212944\ndrwxr-xr-x. 12 rguske rguske        4096 Apr 23 12:39 working-dir\n-rw-r--r--.  1 rguske rguske 62682043392 Apr 23 12:58 mirror_000001.tar\n```\n\nUpload to your mirror registry:\n\n```code\noc mirror -c $(pwd)/imagesetconfiguration.yaml --from file://$(pwd)/mirror/ docker://rguske-rhel9-disco-bastion.disco.local:8443/disco --v2\n```\n\nResults:\n\n```code\n2026/04/24 11:34:43  [INFO]   : === Results ===\n2026/04/24 11:34:43  [INFO]   :  ✓  193 / 193 release images mirrored successfully\n2026/04/24 11:34:43  [INFO]   :  ✓  95 / 95 operator images mirrored successfully\n2026/04/24 11:34:43  [INFO]   :  ✓  7 / 7 additional images mirrored successfully\n2026/04/24 11:34:43  [INFO]   : 📄 Generating IDMS file...\n2026/04/24 11:34:43  [INFO]   : /home/rguske/openshift/mirror/working-dir/cluster-resources/idms-oc-mirror.yaml file created\n2026/04/24 11:34:43  [INFO]   : 📄 Generating ITMS file...\n2026/04/24 11:34:43  [INFO]   : /home/rguske/openshift/mirror/working-dir/cluster-resources/itms-oc-mirror.yaml file created\n2026/04/24 11:34:43  [INFO]   : 📄 Generating CatalogSource file...\n2026/04/24 11:34:43  [INFO]   : /home/rguske/openshift/mirror/working-dir/cluster-resources/cs-redhat-operator-index-v4-21.yaml file created\n2026/04/24 11:34:43  [INFO]   : 📄 Generating ClusterCatalog file...\n2026/04/24 11:34:43  [INFO]   : /home/rguske/openshift/mirror/working-dir/cluster-resources/cc-redhat-operator-index-v4-21.yaml file created\n2026/04/24 11:34:43  [INFO]   : 📄 Generating Signature Configmap...\n2026/04/24 11:34:43  [INFO]   : /home/rguske/openshift/mirror/working-dir/cluster-resources/signature-configmap.json file created\n2026/04/24 11:34:43  [INFO]   : /home/rguske/openshift/mirror/working-dir/cluster-resources/signature-configmap.yaml file created\n2026/04/24 11:34:43  [INFO]   : 📄 Generating UpdateService file...\n2026/04/24 11:34:43  [INFO]   : /home/rguske/openshift/mirror/working-dir/cluster-resources/updateService.yaml file created\n2026/04/24 11:34:43  [INFO]   : mirror time     : 50m26.676667165s\n2026/04/24 11:34:43  [INFO]   : 👋 Goodbye, thank you for using oc-mirror\n```\n\n### unexpected status code 413 Request Entity Too Large\n\nIf this hits you, try using the following options:\n\n```code\noc mirror -c $(pwd)/imagesetconfiguration.yaml --from file://$(pwd)/mirror/ docker://rguske-rhel9-disco-bastion.\ndisco.local:8443/disco --image-timeout 2h --parallel-images=10 --parallel-layers=10 --retry-times=5 --retry-delay=10s --v2\n```\n\nDocumented in [oc-mirror v2 fails with context deadline exceeded when mirroring large images to a local registry in RHOCP 4](https://access.redhat.com/solutions/7130341).\n\n## Installing a disconnected Cluster using the Agent Based Installer\n\n\u003e When you use a disconnected mirror registry, you must add the certificate file that you created previously for your mirror registry to the additionalTrustBundle field of the install-config.yaml file.\n\nWorkflow:\n\n- Create mirror registry content (oc mirror) ✅\n- Create installation assets (install-config.yaml)\n- Create the cluster\n- Apply mirror configuration to the new cluster\n\n### Cluster Preperations\n\nNetwork 192.168.69.0/24\nDNS: 192.168.69.6\nGW: 192.168.69.254\nVLAN ID 69\n\nCollecting the necessary nic information:\n\n| name  | nic | mac | ipv4 | comment |\n|---|---|---|---|---|\n| rguske-ocp42-disco-1  | enp1s0 | 02:d8:6d:0f:3e:dc | 192.168.69.202  | Node 1  |\n| rguske-ocp42-disco-2  | enp1s0 |  02:d8:6d:0f:3e:dd | 192.168.69.203  | Node 2  |\n| rguske-ocp42-disco-3  | enp1s0 | 02:d8:6d:0f:3e:de | 192.168.69.204  | Node 3  |\n\nBaseDomain: disco.local\n\n## Configurations\n\nCreate the `agent-config.yaml as well as the install-config.yaml`\n\n```code\ntree rguske-ocp42-disco/\nrguske-ocp42-disco/\n└── conf\n    ├── agent-config.yaml\n    └── install-config.yaml\n```\n\n```yaml\ncat \u003e agent-config.yaml \u003c\u003c EOF\napiVersion: v1beta1\nkind: AgentConfig\nmetadata:\n  name: rguske-ocp42-disco\nrendezvousIP: 192.168.69.202\nhosts:\n  - hostname: rguske-ocp42-disco-1.disco.local\n    role: master\n    interfaces:\n      - name: enp1s0\n        macAddress: 02:d8:6d:0f:3e:dc\n    networkConfig:\n      interfaces:\n        - name: enp1s0\n          type: ethernet\n          state: up\n          mac-address: 02:d8:6d:0f:3e:dc\n          ipv4:\n            enabled: true\n            address:\n              - ip: 192.168.69.202\n                prefix-length: 24\n            dhcp: false\n      dns-resolver:\n        config:\n          server:\n            - 192.168.69.6\n      routes:\n        config:\n          - destination: 0.0.0.0/0\n            next-hop-address: 192.168.69.254\n            next-hop-interface: enp1s0\n            table-id: 254\n  - hostname: rguske-ocp42-disco-2.disco.local\n    role: master\n    interfaces:\n      - name: enp1s0\n        macAddress: 02:d8:6d:0f:3e:dd\n    networkConfig:\n      interfaces:\n        - name: enp1s0\n          type: ethernet\n          state: up\n          mac-address: 02:d8:6d:0f:3e:dd\n          ipv4:\n            enabled: true\n            address:\n              - ip: 192.168.69.203\n                prefix-length: 24\n            dhcp: false\n      dns-resolver:\n        config:\n          server:\n            - 192.168.69.6\n      routes:\n        config:\n          - destination: 0.0.0.0/0\n            next-hop-address: 192.168.69.254\n            next-hop-interface: enp1s0\n            table-id: 254\n  - hostname: rguske-ocp42-disco-3.disco.local\n    role: master\n    interfaces:\n      - name: enp1s0\n        macAddress: 02:d8:6d:0f:3e:de\n    networkConfig:\n      interfaces:\n        - name: enp1s0\n          type: ethernet\n          state: up\n          mac-address: 02:d8:6d:0f:3e:de\n          ipv4:\n            enabled: true\n            address:\n              - ip: 192.168.69.204\n                prefix-length: 24\n            dhcp: false\n      dns-resolver:\n        config:\n          server:\n            - 192.168.69.6\n      routes:\n        config:\n          - destination: 0.0.0.0/0\n            next-hop-address: 192.168.69.254\n            next-hop-interface: enp1s0\n            table-id: 254\nEOF\n```\n\nThe ssl certificate of the mirror-registry which will be used in the `install-config.yaml` can be found within the `mirror-registry/root/quay-rootCA` folder. Example: `/home/rguske/downloads/mirror-registry/root/quay-rootCA`\n\n```json\n{\n  \"auths\": {\n    \"rguske-rhel9-disco-bastion.rguske.coe.muc.redhat.com:8443\": {\n      \"auth\": \"aW5pdDpyM2RoNHQxIQ==\",\n      \"email\": \"rguske@redhat.com\"\n    }\n  }\n}\n```\n\n```yaml\ncat \u003e install-config.yaml \u003c\u003c EOF\napiVersion: v1\nbaseDomain: disco.local\nImageDigestSources:\n- mirrors:\n  - rguske-rhel9-disco-bastion.disco.local:8443/disco/openshift/release\n  source: quay.io/openshift-release-dev/ocp-v4.0-art-dev\n- mirrors:\n  - rguske-rhel9-disco-bastion.disco.local:8443/disco/openshift/release-images\n  source: quay.io/openshift-release-dev/ocp-release\nadditionalTrustBundle: |\n  -----BEGIN CERTIFICATE-----\n  MIIEHDCCAwSgAwIBAgIUFY/Z+WmgJ+8SIREIa3Cl3FRj9jYwDQYJKoZIhvcNAQEL\n  BQAwgYAxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTERMA8GA1UEBwwITmV3IFlv\n  cmsxDTALBgNVBAoMBFF1YXkxETAPBgNVBAsMCERpdmlzaW9uMS8wLQYDVQQDDCZy\n  Z3Vza2UtcmhlbDktZGlzY28tYmFzdGlvbi5kaXNjby5sb2NhbDAeFw0yNjA0MjQx\n  NDE3MTZaFw0yOTAyMTExNDE3MTZaMIGAMQswCQYDVQQGEwJVUzELMAkGA1UECAwC\n...\n  ZyYMLJyR83M5sD7sVbbuSOkYgNt20ZdIcqigIkyABRkqcahC7kOypXXJbhkj3fYL\n  kyGukgbJRF96hCB9oO8bW3evact/P40arsjHT6qKRIZf0kKm7CYUVRjI4+jlz+oV\n  n7iB3Rs8P16UvuFB2LfWmyNfuu21InZhXLmJ+rZJc0qnpq6Rm8iXAq0n8L5ycCHc\n  gPt4JJQZJ8JP6bSREgAhqfNSngfLj73O1+S2fuN7i3mCQEv0UajEhQgHQtcZ6r1C\n  -----END CERTIFICATE-----\ncompute:\n- name: worker\n  replicas: 0\ncontrolPlane:\n  name: master\n  replicas: 3\nmetadata:\n  name: rguske-ocp42-disco\nnetworking:\n  clusterNetwork:\n    - cidr: 10.128.0.0/14\n      hostPrefix: 23\n  machineNetwork:\n    - cidr: 192.168.69.0/24\n  serviceNetwork:\n    - 172.30.0.0/16\n  networkType: OVNKubernetes\nplatform:\n  baremetal:\n    apiVIPs:\n    - 192.168.69.200\n    ingressVIPs:\n    - 192.168.69.201\nfips: false\npullSecret: '{\n  \"auths\": {\n    \"rguske-rhel9-disco-bastion.disco.local:8443\": {\n      \"auth\": \"aW5pdDpyM2RoNHQxIQ==\",\n      \"email\": \"rguske@redhat.com\"\n    }\n  }\n}'\nsshKey: 'ssh-ed25519 AAAAC3NzaC1lZ...VzGQ/Ur5Ek0v9gF rguske@rguske-rhel9-disco-bastion.rguske.coe.muc.redhat.com'\nEOF\n```\n\nCreate a new `openshift-install-fips` binary which only points to your mirror-registry.\n\n```code\nexport LOCAL_SECRET_JSON='/home/rguske/openshift/new-pull-secret.json' \\\nexport LOCAL_REGISTRY='rguske-rhel9-disco-bastion.disco.local:8443' \\\nexport LOCAL_REPOSITORY='disco/openshift/release-images' \\\nexport OCP_RELEASE='4.21.10' \\\nexport ARCHITECTURE='x86_64'\n```\n\n```code\noc adm release extract -a ${LOCAL_SECRET_JSON} --idms-file=/home/rguske/openshift/mirror/working-dir/cluster-resources/idms-oc-mirror.yaml --command=openshift-install-fips \"${LOCAL_REGISTRY}/${LOCAL_REPOSITORY}:${OCP_RELEASE}-${ARCHITECTURE}\"\n```\n\n```code\ntree -L 1\n.\n├── downloads\n├── oc-mirror-web-app\n├── openshift\n└── openshift-install-fips\n```\n\nValidate it:\n\n```code\n./openshift-install-fips version\n./openshift-install-fips 4.21.10\nbuilt from commit 6285755d199e7aa7bf29db5fe6964ce7f3684ed9\nrelease image rguske-rhel9-disco-bastion.disco.local:8443/disco/openshift/release-images@sha256:5d591a70c92a6dfa3b6b948ffe5e5eac7ab339c49005744006aa0dd9d6d98898\nRelease Image Architecture is unknown\nrelease architecture unknown\ndefault architecture amd64\n```\n\nThis binary will be used for the creation of the agent.iso.\n\n## Create Agent iso\n\nCreate the install-config.yaml and agent-install.yaml file.\n\nRun `./openshift-install-fips agent create image --dir /home/rguske/openshift/rguske-ocp42-disco/conf/`\n\nExample output :\n\n```code\nINFO Configuration has 3 master replicas, 0 arbiter replicas, and 0 worker replicas\nWARNING The imageDigestSources configuration in install-config.yaml should have at least one source field matching the releaseImage value rguske-rhel9-disco-bastion.disco.local:8443/disco/openshift/release-images@sha256:5d591a70c92a6dfa3b6b948ffe5e5eac7ab339c49005744006aa0dd9d6d98898\nINFO The rendezvous host IP (node0 IP) is 192.168.69.202\nINFO Extracting base ISO from release payload\nINFO Base ISO obtained from release and cached at [/home/rguske/.cache/agent/image_cache/coreos-x86_64.iso]\nINFO Consuming Install Config from target directory\nINFO Consuming Agent Config from target directory\nINFO Generated ISO at /home/rguske/openshift/rguske-ocp42-disco/conf/agent.x86_64.iso.\n```\n\nIf you still have problemes with the release-image reference use:\n\n```code\nexport OPENSHIFT_INSTALL_RELEASE_IMAGE_OVERRIDE='rguske-rhel9-disco-bastion.rguske.coe.muc.redhat.com:8443/disco/openshift/release-images@sha256:5d591a70c92a6dfa3b6b948ffe5e5eac7ab339c49005744006aa0dd9d6d98898'\n```\n\nMount the `agent.x86_64.iso` on the machines (BM or VM).\n\nIf you're running the bastion host on e.g. OpenShift Virtualization, you can use the following command in order to upload the ise to a pvc:\n\n```code\nVERSION=$(curl -s https://api.github.com/repos/kubevirt/kubevirt/releases/latest | grep tag_name | cut -d '\"' -f 4)\ncurl -L -o virtctl https://github.com/kubevirt/kubevirt/releases/download/$VERSION/virtctl-$VERSION-linux-amd64\nchmod +x virtctl\nsudo mv virtctl /usr/local/bin/\n```\n\nLogin into your existing OpenShift cluster, change into your project and upload the iso:\n\n```code\nvirtctl image-upload pvc rguskeagentiso-disco --size 2Gi --storage-class coe-netapp-nas --access-mode ReadWriteMany --image-path ~/openshift/rguske-ocp42-disco/conf/agent.x86_64.iso --insecure\n```\n\nBoot the machines and wait until the installation is done.\n\nValidate the installer progress using `./openshift-install-fips wait-for install-complete --dir /home/rguske/openshift/rguske-ocp42-disco/conf/ --log-level=debug`\n\n## Run a `httpd` webserver on the bastion to share the iso\n\nDepending on your environment, providing the created iso can be cumbersome.\n\nOne quick and easy way could be by making it downloadable via a webserver.\n\nInstall `httpd` on the bastion host.\n\n```bash\ndnf install httpd\nsudo systemctl enable --now httpd\nsudo firewall-cmd --permanent --add-service=http\nsudo firewall-cmd --reload\n```\n\nValidate the service is running:\n\n```bash\nsudo ss -tuln | grep :80\ncurl -I http://localhost\nsudo tail -f /var/log/httpd/error_log\n```\n\nCopy (`scp agent.x86_64.iso root@192.168.69.208:/root/download/`) the agent.iso from the mirror registry to the bastion host which has access to the target (ESXi server for example).\n\nCopy the created iso into `/var/www/html/` on the bastion host.\n\nDownload the iso by using e.g. `wget http://\u003cbastion-name/ip\u003e/agent.x86_64.iso`.\n\nExample:\n\n```code\n wget http://bastion-rguske.rguske.coe.muc.redhat.com/agent.x86_64.iso\nConnecting to bastion-rguske.rguske.coe.muc.redhat.com (10.32.96.138:80)\nsaving to 'agent.x86_64.iso'\nagent.x86_64.iso      21%  ********************************************   |  261M  0:00:10 ETA\n```\n\n![disco-cluster-operator](assets/disco-cluster-operators.png)\n\n## Using Operator Lifecycle Manager in disconnected environments\n\nDocs: [Using Operator Lifecycle Manager in disconnected environments](https://docs.redhat.com/en/documentation/openshift_container_platform/4.21/html/disconnected_environments/olm-restricted-networks)\n\nDisable the sources for the default catalogs by adding disableAllDefaultSources: true to the OperatorHub object:\n\n```code\noc patch OperatorHub cluster --type json \\\n    -p '[{\"op\": \"add\", \"path\": \"/spec/disableAllDefaultSources\", \"value\": true}]'\n```\n\nCreate a CatalogSource object that references your index image.\n\n```yaml\noc create -f - \u003c\u003cEOF\napiVersion: operators.coreos.com/v1alpha1\nkind: CatalogSource\nmetadata:\n  name: my-operator-catalog\n  namespace: openshift-marketplace\nspec:\n  sourceType: grpc\n  grpcPodConfig:\n    securityContextConfig: legacy\n  image: rguske-rhel9-disco-bastion.disco.local:8443/disco/redhat/redhat-operator-index:v4.21\n  displayName: My Operator Catalog\n  publisher: rguske-disco-lab\n  updateStrategy:\n    registryPoll:\n      interval: 30m\nEOF\n```\n\nAfter applying the new CatalogSource, delete the old one.\n\nInstall the OpenShift Update Serve Operator and create the instance using the created file which is located in `/home/rguske/openshift/mirror/working-dir/cluster-resources/`:\n\nupdateService.yaml\n\n```yaml\napiVersion: updateservice.operator.openshift.io/v1\nkind: UpdateService\nmetadata:\n  annotations:\n    createdAt: Monday, 27-Apr-26 15:46:08 UTC\n    createdBy: oc-mirror v2\n    oc-mirror_version: 4.21.0-202604140043.p2.g12f1b06.assembly.stream.el9-12f1b06\n  name: update-service-oc-mirror\nspec:\n  graphDataImage: rguske-rhel9-disco-bastion.disco.local:8443/disco/openshift/graph-image:latest\n  releases: rguske-rhel9-disco-bastion.disco.local:8443/disco/openshift/release-images\n  replicas: 2\nstatus: {}\n```\n\nThe update service will be available after applying this configuration but it'll not trust your registry. Therefore, a ConfigMap with the root certificate of yopur mirror registry needs to be created.\n\nDocs: [Configuring access to a secured registry for the OpenShift Update Service](https://docs.redhat.com/en/documentation/openshift_container_platform/4.21/html/disconnected_environments/updating-a-cluster-in-a-disconnected-environment#registry-configuration-for-update-service_updating-disconnected-cluster-osus)\nDocs: [Configuring additional trust stores for image registry access ](https://docs.redhat.com/en/documentation/openshift_container_platform/4.21/html-single/registry/index#images-configuration-cas_configuring-registry-operator)\n\nYou can add references to a config map that has additional certificate authorities (CAs) to be trusted during image registry access to the `image.config.openshift.io/cluster` custom resource (CR).\n\nImportant is, that the name of your mirror registry is included as well as the name `updateservice-registry`, which will be picked up by the CLuster Service Operator.\n\n```yaml\noc -n openshift-config apply -f - \u003c\u003cEOF\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: my-mirror-registry-ca\ndata:\n  updateservice-registry: |\n    -----BEGIN CERTIFICATE-----\n    MIIEHDCCAwSgAwIBAgIUFY/Z+WmgJ+8SIREIa3Cl3FRj9jYwDQYJKoZIhvcNAQEL\n    BQAwgYAxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTERMA8GA1UEBwwITmV3IFlv\n    cmsxDTALBgNVBAoMBFF1YXkxETAPBgNVBAsMCERpdmlzaW9uMS8wLQYDVQQDDCZy\n    Z3Vza2UtcmhlbDktZGlzY28tYmFzdGlvbi5kaXNjby5sb2NhbDAeFw0yNjA0MjQx\n    NDE3MTZaFw0yOTAyMTExNDE3MTZaMIGAMQswCQYDVQQGEwJVUzELMAkGA1UECAwC\n    VkExETAPBgNVBAcMCE5ldyBZb3JrMQ0wCwYDVQQKDARRdWF5MREwDwYDVQQLDAhE\n...\n    AQUFBwMBMDEGA1UdEQQqMCiCJnJndXNrZS1yaGVsOS1kaXNjby1iYXN0aW9uLmRp\n    c2NvLmxvY2FsMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFJvWooMOH9HM\n    69OZwSFGVXzbJ5wrMA0GCSqGSIb3DQEBCwUAA4IBAQA6BS7YqGzv0TYbLWs0iG3r\n    YTQeVGt1dZK5uf8k2mRHiNkmPbNAsSeEb8eh+Wes3MNn6iT3fC55kZVdGL21jzYu\n    ZyYMLJyR83M5sD7sVbbuSOkYgNt20ZdIcqigIkyABRkqcahC7kOypXXJbhkj3fYL\n    kyGukgbJRF96hCB9oO8bW3evact/P40arsjHT6qKRIZf0kKm7CYUVRjI4+jlz+oV\n    n7iB3Rs8P16UvuFB2LfWmyNfuu21InZhXLmJ+rZJc0qnpq6Rm8iXAq0n8L5ycCHc\n    gPt4JJQZJ8JP6bSREgAhqfNSngfLj73O1+S2fuN7i3mCQEv0UajEhQgHQtcZ6r1C\n    -----END CERTIFICATE-----\n  rguske-rhel9-disco-bastion.rguske.coe.muc.redhat.com..8443: |\n    -----BEGIN CERTIFICATE-----\n    MIIEHDCCAwSgAwIBAgIUFY/Z+WmgJ+8SIREIa3Cl3FRj9jYwDQYJKoZIhvcNAQEL\n    BQAwgYAxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTERMA8GA1UEBwwITmV3IFlv\n    cmsxDTALBgNVBAoMBFF1YXkxETAPBgNVBAsMCERpdmlzaW9uMS8wLQYDVQQDDCZy\n    Z3Vza2UtcmhlbDktZGlzY28tYmFzdGlvbi5kaXNjby5sb2NhbDAeFw0yNjA0MjQx\n...\n    c2NvLmxvY2FsMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFJvWooMOH9HM\n    69OZwSFGVXzbJ5wrMA0GCSqGSIb3DQEBCwUAA4IBAQA6BS7YqGzv0TYbLWs0iG3r\n    YTQeVGt1dZK5uf8k2mRHiNkmPbNAsSeEb8eh+Wes3MNn6iT3fC55kZVdGL21jzYu\n    ZyYMLJyR83M5sD7sVbbuSOkYgNt20ZdIcqigIkyABRkqcahC7kOypXXJbhkj3fYL\n    kyGukgbJRF96hCB9oO8bW3evact/P40arsjHT6qKRIZf0kKm7CYUVRjI4+jlz+oV\n    n7iB3Rs8P16UvuFB2LfWmyNfuu21InZhXLmJ+rZJc0qnpq6Rm8iXAq0n8L5ycCHc\n    gPt4JJQZJ8JP6bSREgAhqfNSngfLj73O1+S2fuN7i3mCQEv0UajEhQgHQtcZ6r1C\n    -----END CERTIFICATE-----\nEOF\n```\n\nAfter creating the `ConfigMap`, edit the CR `config.openshift.io/v1` named cluster with the new `additionalTrustedCA`\n\n```yaml\n[...]\nspec:\n  additionalTrustedCA:\n    name: my-mirror-registry-ca\n```\n\nCheck the pods within the namespace `openshift-update-service`:\n\n```code\noc -n openshift-update-service get pods\nNAME                                      READY   STATUS    RESTARTS   AGE\ngraph-data-tag-digest                     1/1     Running   0          2m17s\nupdate-service-oc-mirror-98764cbd-cj2sd   2/2     Running   0          8m4s\nupdate-service-oc-mirror-98764cbd-t87tf   2/2     Running   0          8m4s\nupdateservice-operator-74d959fd7d-qzj8s   1/1     Running   0          46m\n```\n\nThe next step is to update the Cluster Update Service with the new `route` object.\n\n```code\noc -n openshift-update-service get route\nNAME                             HOST/PORT                                                                                     PATH   SERVICES                                 PORT            TERMINATION   WILDCARD\nupdate-service-oc-mirror-route   update-service-oc-mirror-route-openshift-update-service.apps.rguske-ocp42-disco.disco.local          update-service-oc-mirror-policy-engine   policy-engine   edge/None     None\n```\n\nAfter updating the `route` via the WebConsole in Administration - Cluster Settings - Upstream Configuration, the Service will complain about the not trusted cluster certificate.\n\nIt is necessary to patch the cluster-wide `proxy` configuration with a config map object which contains the cluster self-signed certificate.\n\nCreate the `cm`:\n\n```yaml\noc apply -f - \u003c\u003cEOF\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: ocp-disco-ingress-certs\n  namespace: openshift-config\ndata:\n  ca-bundle.crt: |\n    # MyPrivateCA (root.crt)\n    -----BEGIN CERTIFICATE-----\n   zzzzz\n    -----END CERTIFICATE-----\nEOF\n```\n\nPatch the `proxy` object:\n\n```code\noc patch proxy/cluster \\\n     --type=merge \\\n     --patch='{\"spec\":{\"trustedCA\":{\"name\":\"user-ca-bundle\"}}}'\n```\n\nCheck the updates to the cluster operators:\n\n```code\noc get co -w\n```\n\nAfter a successful reconciliation, the update graph should look good.\n\n## Troubleshooting\n\nTypical disconnected blockers in OpenShift agent-based installs are:\n\n- release image not mirrored correctly\n- OS image URL inaccessible\n- missing release signatures\n- mirrored registry CA not trusted\n- incorrect imageContentSources / ImageDigestMirrorSet\n- registry auth not included in pull secret\n- missing boot artifacts in disconnected cache\n- cluster never transitions from insufficient → ready\n\n### Networking\n\n`ssh -i ~/.ssh/id_ed25519 core@rguske-ocp42-disco-2.disco.local`\n\nDNS:\n\n```code\n[core@rguske-ocp42-disco-1 ~]$ dig +short rguske-ocp42-disco-2.disco.local\n192.168.69.203\n[core@rguske-ocp42-disco-1 ~]$ dig +short rguske-ocp42-disco-1.disco.local\n192.168.69.202\n[core@rguske-ocp42-disco-1 ~]$ dig +short rguske-ocp42-disco-3.disco.local\n192.168.69.204\n[core@rguske-ocp42-disco-1 ~]$ dig +short api.rguske-ocp42-disco.disco.local\n192.168.69.200\n[core@rguske-ocp42-disco-1 ~]$ dig +short console.apps.rguske-ocp42-disco.disco.local\n192.168.69.201\n```\n\nPing:\n\n```code\n[core@rguske-ocp42-disco-2 ~]$ ping -c 3 rguske-ocp42-disco-1\nPING rguske-ocp42-disco-1.disco.local (192.168.69.202) 56(84) bytes of data.\n64 bytes from rguske-ocp42-disco-1.disco.local (192.168.69.202): icmp_seq=1 ttl=64 time=0.311 ms\n64 bytes from rguske-ocp42-disco-1.disco.local (192.168.69.202): icmp_seq=2 ttl=64 time=0.435 ms\n64 bytes from rguske-ocp42-disco-1.disco.local (192.168.69.202): icmp_seq=3 ttl=64 time=0.341 ms\n\n--- rguske-ocp42-disco-1.disco.local ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2003ms\nrtt min/avg/max/mdev = 0.311/0.362/0.435/0.052 ms\n[core@rguske-ocp42-disco-2 ~]$ ping -c 3 rguske-ocp42-disco-2\nPING rguske-ocp42-disco-2.disco.local (192.168.69.203) 56(84) bytes of data.\n64 bytes from rguske-ocp42-disco-2.disco.local (192.168.69.203): icmp_seq=1 ttl=64 time=0.052 ms\n64 bytes from rguske-ocp42-disco-2.disco.local (192.168.69.203): icmp_seq=2 ttl=64 time=0.103 ms\n64 bytes from rguske-ocp42-disco-2.disco.local (192.168.69.203): icmp_seq=3 ttl=64 time=0.082 ms\n\n--- rguske-ocp42-disco-2.disco.local ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2027ms\nrtt min/avg/max/mdev = 0.052/0.079/0.103/0.020 ms\n[core@rguske-ocp42-disco-2 ~]$ ping -c 3 rguske-ocp42-disco-3\nPING rguske-ocp42-disco-3.disco.local (192.168.69.204) 56(84) bytes of data.\n64 bytes from rguske-ocp42-disco-3.disco.local (192.168.69.204): icmp_seq=1 ttl=64 time=0.360 ms\n64 bytes from rguske-ocp42-disco-3.disco.local (192.168.69.204): icmp_seq=2 ttl=64 time=1.24 ms\n64 bytes from rguske-ocp42-disco-3.disco.local (192.168.69.204): icmp_seq=3 ttl=64 time=1.25 ms\n```\n\n### Logs\n\nLogs:\n\nOn the Rendevouz host run `journalctl assisted-service.service -f`\n\nOn the worker nodes run `journalctl -b -f -u release-image.service -u bootkube.service -u node-image-pull.service -f`\n\n### Cluster Status validations\n\nInspect cluster validation status directly.\n\n```code\ncurl -s http://localhost:8090/api/assisted-install/v2/clusters | jq\n```\n\nAuthorization failed:\n\n```code\n{ \"code\": 401, \"message\": \"unauthenticated for invalid credentials\" }\n```\n\nThe assisted-service API is protected, so the unauthenticated response is expected.\n\nObtain the token:\n\n```code\npodman inspect 580  | jq '.[0].Config.Env' | grep USER_AUTH_TOKEN\n  \"USER_AUTH_TOKEN=eyJhbGciOiJFUzI1Ni...OFQ1DwtPZBRY3UKEswjgXEJSbrQ\",\n```\n\nCheck the cluster status:\n\n```code\nTOKEN=\"eyJhbGciOiJFUzI1Ni...OFQ1DwtPZBRY3UKEswjgXEJSbrQ\"\n\ncurl -s \\\n  -H \"Authorization: $TOKEN\" \\\n  http://localhost:8090/api/assisted-install/v2/clusters | jq\n```\n\nThe status summary:\n\n```json\n[\n  {\n    \"api_vips\": [\n      {\n        \"cluster_id\": \"ceeb9d66-c894-4224-adcc-72283fd213f4\",\n        \"ip\": \"192.168.69.200\",\n        \"verification\": \"succeeded\"\n      }\n    ],\n    \"base_dns_domain\": \"disco.local\",\n    \"cluster_networks\": [\n      {\n        \"cidr\": \"10.128.0.0/14\",\n        \"cluster_id\": \"ceeb9d66-c894-4224-adcc-72283fd213f4\",\n        \"host_prefix\": 23\n      }\n    ],\n    \"connectivity_majority_groups\": \"{\\\"majority_groups\\\":{\\\"192.168.69.0/24\\\":[\\\"35ddb3ef-1234-5220-b32c-331d1cb817a3\\\",\\\"96f0818e-1d12-5a86-ab09-ddaacf305552\\\",\\\"9bda5859-ae9b-5c6f-87ac-8904cc41b857\\\"],\\\"IPv4\\\":[\\\"35ddb3ef-1234-5220-b32c-331d1cb817a3\\\",\\\"96f0818e-1d12-5a86-ab09-ddaacf305552\\\",\\\"9bda5859-ae9b-5c6f-87ac-8904cc41b857\\\"],\\\"IPv6\\\":[]},\\\"l3_connected_addresses\\\":{\\\"35ddb3ef-1234-5220-b32c-331d1cb817a3\\\":[\\\"192.168.69.203\\\"],\\\"96f0818e-1d12-5a86-ab09-ddaacf305552\\\":[\\\"192.168.69.204\\\"],\\\"9bda5859-ae9b-5c6f-87ac-8904cc41b857\\\":[\\\"192.168.69.202\\\"]}}\",\n    \"control_plane_count\": 3,\n    \"controller_logs_collected_at\": \"0001-01-01T00:00:00.000Z\",\n    \"controller_logs_started_at\": \"0001-01-01T00:00:00.000Z\",\n    \"cpu_architecture\": \"x86_64\",\n    \"created_at\": \"2026-04-27T06:55:16.101392Z\",\n    \"deleted_at\": null,\n    \"disk_encryption\": {\n      \"enable_on\": \"none\",\n      \"mode\": \"tpmv2\"\n    },\n    \"email_domain\": \"Unknown\",\n    \"enabled_host_count\": 3,\n    \"feature_usage\": \"{\\\"Hyperthreading\\\":{\\\"data\\\":{\\\"hyperthreading_enabled\\\":\\\"all\\\"},\\\"id\\\":\\\"HYPERTHREADING\\\",\\\"name\\\":\\\"Hyperthreading\\\"},\\\"OVN network type\\\":{\\\"id\\\":\\\"OVN_NETWORK_TYPE\\\",\\\"name\\\":\\\"OVN network type\\\"},\\\"Static Network Config\\\":{\\\"id\\\":\\\"STATIC_NETWORK_CONFIG\\\",\\\"name\\\":\\\"Static Network Config\\\"}}\",\n    \"high_availability_mode\": \"Full\",\n    \"host_networks\": null,\n    \"hosts\": [],\n    \"href\": \"/api/assisted-install/v2/clusters/ceeb9d66-c894-4224-adcc-72283fd213f4\",\n    \"hyperthreading\": \"all\",\n    \"id\": \"ceeb9d66-c894-4224-adcc-72283fd213f4\",\n    \"ignition_endpoint\": {},\n    \"image_info\": {\n      \"created_at\": \"2026-04-27T06:55:16.101392Z\",\n      \"expires_at\": \"0001-01-01T00:00:00.000Z\"\n    },\n    \"ingress_vips\": [\n      {\n        \"cluster_id\": \"ceeb9d66-c894-4224-adcc-72283fd213f4\",\n        \"ip\": \"192.168.69.201\",\n        \"verification\": \"succeeded\"\n      }\n    ],\n    \"install_completed_at\": \"0001-01-01T00:00:00.000Z\",\n    \"install_started_at\": \"0001-01-01T00:00:00.000Z\",\n    \"ip_collisions\": \"{}\",\n    \"kind\": \"Cluster\",\n    \"last-installation-preparation\": {},\n    \"load_balancer\": {\n      \"type\": \"cluster-managed\"\n    },\n    \"machine_networks\": [\n      {\n        \"cidr\": \"192.168.69.0/24\",\n        \"cluster_id\": \"ceeb9d66-c894-4224-adcc-72283fd213f4\"\n      }\n    ],\n    \"monitored_operators\": [\n      {\n        \"bundles\": null,\n        \"cluster_id\": \"ceeb9d66-c894-4224-adcc-72283fd213f4\",\n        \"name\": \"console\",\n        \"operator_type\": \"builtin\",\n        \"status_updated_at\": \"0001-01-01T00:00:00.000Z\",\n        \"timeout_seconds\": 3600\n      }\n    ],\n    \"name\": \"rguske-ocp42-disco\",\n    \"network_type\": \"OVNKubernetes\",\n    \"ocp_release_image\": \"rguske-rhel9-disco-bastion.disco.local:8443/disco/openshift/release-images@sha256:5d591a70c92a6dfa3b6b948ffe5e5eac7ab339c49005744006aa0dd9d6d98898\",\n    \"openshift_version\": \"4.21.10\",\n    \"org_soft_timeouts_enabled\": true,\n    \"platform\": {\n      \"external\": {},\n      \"type\": \"baremetal\"\n    },\n    \"progress\": {\n      \"finalizing_stage_started_at\": \"0001-01-01T00:00:00.000Z\"\n    },\n    \"pull_secret_set\": true,\n    \"ready_host_count\": 1,\n    \"schedulable_masters\": false,\n    \"schedulable_masters_forced_true\": true,\n    \"service_networks\": [\n      {\n        \"cidr\": \"172.30.0.0/16\",\n        \"cluster_id\": \"ceeb9d66-c894-4224-adcc-72283fd213f4\"\n      }\n    ],\n    \"ssh_public_key\": \"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIODE9JnscEgdhihWM2xsqlqsRgBjkVzGQ/Ur5Ek0v9gF rguske@rguske-rhel9-disco-bastion.rguske.coe.muc.redhat.com\",\n    \"status\": \"insufficient\",\n    \"status_info\": \"Cluster is not ready for install\",\n    \"status_updated_at\": \"2026-04-27T06:55:16.099Z\",\n    \"total_host_count\": 3,\n    \"updated_at\": \"2026-04-27T06:56:30.886142Z\",\n    \"user_managed_networking\": false,\n    \"user_name\": \"admin\",\n    \"validations_info\": \"{\\\"configuration\\\":[{\\\"id\\\":\\\"platform-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"Platform requirements satisfied\\\"},{\\\"id\\\":\\\"pull-secret-set\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"The pull secret is set.\\\"}],\\\"hosts-data\\\":[{\\\"id\\\":\\\"all-hosts-are-ready-to-install\\\",\\\"status\\\":\\\"failure\\\",\\\"message\\\":\\\"The cluster has hosts that are not ready to install.\\\"},{\\\"id\\\":\\\"sufficient-masters-count\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"The cluster has the exact amount of dedicated control plane nodes.\\\"}],\\\"network\\\":[{\\\"id\\\":\\\"api-vips-defined\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"API virtual IPs are defined.\\\"},{\\\"id\\\":\\\"api-vips-valid\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"api vips 192.168.69.200 belongs to the Machine CIDR and is not in use.\\\"},{\\\"id\\\":\\\"cluster-cidr-defined\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"The Cluster Network CIDR is defined.\\\"},{\\\"id\\\":\\\"dns-domain-defined\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"The base domain is defined.\\\"},{\\\"id\\\":\\\"ingress-vips-defined\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"Ingress virtual IPs are defined.\\\"},{\\\"id\\\":\\\"ingress-vips-valid\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"ingress vips 192.168.69.201 belongs to the Machine CIDR and is not in use.\\\"},{\\\"id\\\":\\\"machine-cidr-defined\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"The Machine Network CIDR is defined.\\\"},{\\\"id\\\":\\\"machine-cidr-equals-to-calculated-cidr\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"The Cluster Machine CIDR is equivalent to the calculated CIDR.\\\"},{\\\"id\\\":\\\"network-prefix-valid\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"The Cluster Network prefix is valid.\\\"},{\\\"id\\\":\\\"network-type-valid\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"The cluster has a valid network type\\\"},{\\\"id\\\":\\\"networks-same-address-families\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"Same address families for all networks.\\\"},{\\\"id\\\":\\\"no-cidrs-overlapping\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"No CIDRS are overlapping.\\\"},{\\\"id\\\":\\\"ntp-server-configured\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"No ntp problems found\\\"},{\\\"id\\\":\\\"service-cidr-defined\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"The Service Network CIDR is defined.\\\"}],\\\"operators\\\":[{\\\"id\\\":\\\"amd-gpu-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"amd-gpu is disabled\\\"},{\\\"id\\\":\\\"authorino-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"authorino is disabled\\\"},{\\\"id\\\":\\\"cluster-observability-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"cluster-observability is disabled\\\"},{\\\"id\\\":\\\"cnv-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"cnv is disabled\\\"},{\\\"id\\\":\\\"fence-agents-remediation-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"fence-agents-remediation is disabled\\\"},{\\\"id\\\":\\\"kmm-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"kmm is disabled\\\"},{\\\"id\\\":\\\"kube-descheduler-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"kube-descheduler is disabled\\\"},{\\\"id\\\":\\\"loki-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"loki is disabled\\\"},{\\\"id\\\":\\\"lso-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"lso is disabled\\\"},{\\\"id\\\":\\\"lvm-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"lvm is disabled\\\"},{\\\"id\\\":\\\"mce-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"mce is disabled\\\"},{\\\"id\\\":\\\"metallb-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"metallb is disabled\\\"},{\\\"id\\\":\\\"mtv-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"mtv is disabled\\\"},{\\\"id\\\":\\\"nmstate-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"nmstate is disabled\\\"},{\\\"id\\\":\\\"node-feature-discovery-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"node-feature-discovery is disabled\\\"},{\\\"id\\\":\\\"node-healthcheck-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"node-healthcheck is disabled\\\"},{\\\"id\\\":\\\"node-maintenance-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"node-maintenance is disabled\\\"},{\\\"id\\\":\\\"numa-resources-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"numaresources is disabled\\\"},{\\\"id\\\":\\\"nvidia-gpu-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"nvidia-gpu is disabled\\\"},{\\\"id\\\":\\\"oadp-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"oadp is disabled\\\"},{\\\"id\\\":\\\"odf-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"odf is disabled\\\"},{\\\"id\\\":\\\"openshift-ai-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"openshift-ai is disabled\\\"},{\\\"id\\\":\\\"openshift-logging-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"openshift-logging is disabled\\\"},{\\\"id\\\":\\\"osc-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"osc is disabled\\\"},{\\\"id\\\":\\\"pipelines-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"pipelines is disabled\\\"},{\\\"id\\\":\\\"self-node-remediation-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"self-node-remediation is disabled\\\"},{\\\"id\\\":\\\"serverless-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"serverless is disabled\\\"},{\\\"id\\\":\\\"servicemesh-requirements-satisfied\\\",\\\"status\\\":\\\"success\\\",\\\"message\\\":\\\"servicemesh is disabled\\\"}]}\",\n    \"vip_dhcp_allocation\": false\n  }\n]\n```\n\nThe `status_info` is telling something:\n\n```code\nstatus: insufficient\nstatus_info: Cluster is not ready for install\n```\n\nSpecifically:\n\n```code\n\"all-hosts-are-ready-to-install\" = failure\n\"The cluster has hosts that are not ready to install.\"\n```\n\nThe next step is to inspect host validation failures:\n\n```code\ncurl -s \\\n  -H \"Authorization: $TOKEN\" \\\n  http://localhost:8090/api/assisted-install/v2/clusters/ceeb9d66-c894-4224-adcc-72283fd213f4/hosts \\\n| jq '.[] | {hostname: .requested_hostname, status: .status, status_info: .status_info, validations: .validations_info}'\n```\n\nMy cluster was stuck in validation progress because of a simple \"copy/paste\" mistake at the hostname section. I had two times the same hostname configured...\n\n```code\nAffected hosts:\n\nHost ID 35ddb3ef-...\nHost ID 96f0818e-...\n\nValidation failure:\n\nhostname-unique = failure\nHostname rguske-ocp42-disco-2.disco.local is not unique in cluster\n```\n\nIf the discovery was successful, run: `journalctl -b -f -u release-image.service -u bootkube.service -u node-image-pull.service -f`\n\n### Firewall is blocking images from pulling\n\nI faced the issue that my disconnected cluster couldn't pull images from my mirror registry anymore. Basic from one of the nodes checks:\n\n```code\nnc -vz rguske-rhel9-disco-bastion.disco.local 8443\n```\n\n```code\ncurl -vk https://rguske-rhel9-disco-bastion.disco.local:8443/v2/\n```\n\nOn the mirror registry host:\n\n```code\nsudo ss -tulpn | grep 8443\n```\n\n```code\nsudo firewall-cmd --list-all\n```\n\n```code\nsudo firewall-cmd --permanent --add-port=8443/tcp\nsudo firewall-cmd --reload\n```\n\nOpening port 8443 again helped:\n\n```code\nnc -vz rguske-rhel9-disco-bastion.disco.local 8443\nNcat: Version 7.92 ( https://nmap.org/ncat )\nNcat: Connected to 192.168.69.208:8443.\nNcat: 0 bytes sent, 0 bytes received in 0.02 seconds.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frguske%2Fopenshift-agent-based-installer-airgapped","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frguske%2Fopenshift-agent-based-installer-airgapped","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frguske%2Fopenshift-agent-based-installer-airgapped/lists"}