Here’s a complete step-by-step guide for installing OKD (the upstream OpenShift) on a single Azure VM — this is an SNO (Single Node OpenShift/OKD) style install using the Agent-Based Installer.
Prerequisites
Local machine (your laptop/workstation)
openshift-installbinary (OKD build)ocCLIazCLI, authenticated to your Azure subscriptionssh-keygenkey pair ready
Azure VM spec (minimum for SNO)
| Resource | Minimum |
|---|---|
| vCPUs | 8 |
| RAM | 16 GB (32 GB recommended) |
| OS Disk | 120 GB SSD |
| OS | RHCOS (installed by OKD — you don’t pick the OS) |
Step 1 — Create the Azure VM (bare metal style)
# VariablesRG="okd-snо-rg"LOCATION="canadacentral"VM_NAME="okd-sno"VNET="okd-vnet"SUBNET="okd-subnet"# Resource groupaz group create --name $RG --location $LOCATION# VNet + Subnetaz network vnet create \ --resource-group $RG \ --name $VNET \ --address-prefix 10.0.0.0/16 \ --subnet-name $SUBNET \ --subnet-prefix 10.0.1.0/24# Public IP (static)az network public-ip create \ --resource-group $RG \ --name okd-pip \ --sku Standard \ --allocation-method Static# NSG + rulesaz network nsg create --resource-group $RG --name okd-nsgfor PORT in 22 80 443 6443 22623; do az network nsg rule create \ --resource-group $RG \ --nsg-name okd-nsg \ --name "allow-$PORT" \ --priority $((1000 + PORT % 1000)) \ --destination-port-ranges $PORT \ --access Allow --protocol Tcpdone# NICaz network nic create \ --resource-group $RG \ --name okd-nic \ --vnet-name $VNET \ --subnet $SUBNET \ --public-ip-address okd-pip \ --network-security-group okd-nsg# VM (no OS — we'll boot from ISO)az vm create \ --resource-group $RG \ --name $VM_NAME \ --nics okd-nic \ --size Standard_D8s_v3 \ --os-disk-size-gb 120 \ --image "RedHat:RHEL:8-lvm-gen2:latest" \ # placeholder — replaced by RHCOS --admin-username azureuser \ --ssh-key-values ~/.ssh/id_rsa.pub \ --storage-sku Premium_LRS
In practice for OKD SNO you boot from the agent ISO — the VM image above is just to get the disk. We’ll write RHCOS over it.
Step 2 — Get the OKD installer & pull secret
# Download latest OKD releaseOKD_VERSION="4.15.0-0.okd-2024-03-10-010116"wget https://github.com/okd-project/okd/releases/download/${OKD_VERSION}/openshift-install-linux-${OKD_VERSION}.tar.gztar xvf openshift-install-linux-*.tar.gzsudo mv openshift-install /usr/local/bin/# Pull secret — OKD uses a fake/empty one (no Red Hat subscription needed)# Use this minimal pull secret:cat > pull-secret.json << 'EOF'{"auths":{"fake":{"auth":"aWQ6cGFzcw=="}}}EOF
Step 3 — Generate install-config.yaml
mkdir ~/okd-installcat > ~/okd-install/install-config.yaml << EOFapiVersion: v1baseDomain: yourdomain.com # e.g. azure.example.commetadata: name: okd-sno # cluster namecompute: - name: worker replicas: 0 # SNO = 0 workerscontrolPlane: name: master replicas: 1 # SNO = 1 master platform: baremetal: {}platform: none: {} # Agent-based uses "none"pullSecret: '{"auths":{"fake":{"auth":"aWQ6cGFzcw=="}}}'sshKey: '$(cat ~/.ssh/id_rsa.pub)'EOF
Step 4 — Generate the Agent ISO
# Copy install-config (installer consumes it)cp ~/okd-install/install-config.yaml ~/okd-install/# Generate agent-config.yaml for SNOcat > ~/okd-install/agent-config.yaml << EOFapiVersion: v1alpha1kind: AgentConfigmetadata: name: okd-snorendezvousIP: 10.0.1.10 # VM's private IPhosts: - hostname: okd-sno role: master interfaces: - name: eth0 macAddress: "$(az network nic show -g $RG -n okd-nic --query 'macAddress' -o tsv | tr '[:upper:]' '[:lower:]')" networkConfig: interfaces: - name: eth0 type: ethernet state: up ipv4: enabled: true address: - ip: 10.0.1.10 prefix-length: 24 dhcp: false dns-resolver: config: server: - 168.63.129.16 # Azure DNS routes: config: - destination: 0.0.0.0/0 next-hop-address: 10.0.1.1 next-hop-interface: eth0EOF# Generate the ISOopenshift-install agent create image --dir ~/okd-install/# Output: ~/okd-install/agent.x86_64.iso
Step 5 — Boot the VM from the Agent ISO
# Upload ISO to Azure Storageaz storage account create --name okdiso$RANDOM --resource-group $RG --sku Standard_LRSSTORAGE_ACCOUNT=$(az storage account list -g $RG --query '[0].name' -o tsv)az storage container create --name isos --account-name $STORAGE_ACCOUNTaz storage blob upload \ --account-name $STORAGE_ACCOUNT \ --container-name isos \ --name agent.iso \ --file ~/okd-install/agent.x86_64.isoISO_URL=$(az storage blob url \ --account-name $STORAGE_ACCOUNT \ --container-name isos \ --name agent.iso -o tsv)# Attach as virtual DVD and set boot orderaz vm disk attach \ --resource-group $RG \ --vm-name $VM_NAME \ --new --sku Premium_LRS \ --lun 1 --size-gb 2 # not needed — use virtual media approach below# Easier: use az vm run-command or Serial Console to dd the ISO to disk# OR use Azure's trusted launch + boot from ISO via az vm boot-diagnostics
Tip for Azure: The cleanest approach is to use a Gen1 VM and attach the ISO as a managed disk image, or use
ddvia serial console to write the ISO to a secondary disk and set it as the boot device in BIOS via Azure Serial Console.
# On the VM via Serial Console — write ISO to disk and rebootsudo dd if=/dev/cdrom of=/dev/sda bs=512k status=progress# OR if uploaded as blob, stream directly:sudo curl -o /dev/sdb "$ISO_URL"sudo reboot
Step 6 — DNS records (critical)
OKD requires these DNS records pointing to your VM’s public IP:
api.okd-sno.yourdomain.com → <public-ip>*.apps.okd-sno.yourdomain.com → <public-ip>
PUBLIC_IP=$(az network public-ip show -g $RG -n okd-pip --query ipAddress -o tsv)echo "Set these DNS A records to: $PUBLIC_IP"echo " api.okd-sno.yourdomain.com"echo " *.apps.okd-sno.yourdomain.com"
Step 7 — Monitor the install
# Watch installation progress from your local machineopenshift-install agent wait-for bootstrap-complete --dir ~/okd-install/ --log-level=info# Then wait for full install completionopenshift-install agent wait-for install-complete --dir ~/okd-install/ --log-level=info# Expected output when done:# INFO Install complete!# INFO To access the cluster as the system:admin user use 'export KUBECONFIG=...'# INFO Access the OpenShift web-console: https://console-openshift-console.apps.okd-sno.yourdomain.com# INFO Login to the console with user: kubeadmin, and password: XXXXX-XXXXX-XXXXX-XXXXX
Step 8 — Access the cluster
export KUBECONFIG=~/okd-install/auth/kubeconfig# Verifyoc get nodes# NAME STATUS ROLES AGE VERSION# okd-sno Ready control-plane,master,worker 10m v1.28.xoc get clusteroperators # all should be Available=Trueoc whoami # system:admin# Web console# https://console-openshift-console.apps.okd-sno.yourdomain.com# user: kubeadmin# pass: cat ~/okd-install/auth/kubeadmin-password
Common Issues
| Problem | Fix |
|---|---|
| Bootstrap never completes | Check DNS — api.* record is the most common miss |
Pods stuck Pending | SNO has no workers — ensure replicas: 0 for compute |
CrashLoopBackOff on etcd | VM has < 16 GB RAM or disk I/O too slow (use Premium SSD) |
| Console unreachable | NSG missing port 443 or *.apps wildcard DNS not set |
| Clock skew errors | Sync VM time — Azure VMs can drift during install |
Total install time: ~45–60 minutes after the VM boots from the ISO. The bulk of that is the cluster operators coming up one by one.