KubeVirt On Mac

One of my favourite up stream Open Source communities is KubeVirt, origionally from Red Hat, it become a CNCF sandbox project in 2019. For those unfamilar with KubeVirt, it essentially has a focus on providing a capability to run virtual images inside containers, this has a number of benefits including the opportunity to have a single approach to managing their application platform. In this post, I’d like to provide an overview for those, like myself who are Apple MacOS users / developer, wanting to contribute to KubeVirt.

There are some pre-requisites that must be met:

  • You must install a local Kubernetes cluster, typically this would involve installing kind or minikube . There is a very good quickstart guide for both on the KubeVirt site.
  • Next, ensure the kubectl command line tool is installed and is pointing at the local cluster.
  • I then recommend installing the virtctl tool, which will be used to create / manage virtual images – the tool can be deployed as a kubectl plugin, which I prefer as it provides consistency for kubernetes management (one tool to use).
  • You must have the GO language installed.
  • You need a container management app, such as Docker or Podman.

To simply how I meet the pre-requisites, I decided to use Homebrew, and with the help of my AI tools, generated a script to automate my setup (which has the added benefit of having a repeatable process if I decide to remove KubeVirt etc).

The script looks like this :

#!/usr/bin/env bash
set -euo pipefail

CLUSTER_NAME="kubevirt-dev"
REPO_DIR="${HOME}/src/kubevirt"
GOPATH_DEFAULT="${HOME}/go"

echo "==> KubeVirt macOS dev bootstrap starting..."

# --- Helper functions --------------------------------------------------------

need() {
  if ! command -v "$1" >/dev/null 2>&1; then
    return 1
  fi
  return 0
}

brew_install_if_missing() {
  local pkg="$1"
  if ! brew list --formula "$pkg" >/dev/null 2>&1; then
    echo "==> Installing ${pkg} with Homebrew..."
    brew install "$pkg"
  else
    echo "==> ${pkg} already installed."
  fi
}

# --- Homebrew ----------------------------------------------------------------

if ! need brew; then
  echo "!! Homebrew is required but not installed."
  echo "   Install from https://brew.sh and re-run this script."
  exit 1
fi

# --- Core tools --------------------------------------------------------------

brew_install_if_missing git
brew_install_if_missing go
brew_install_if_missing make
brew_install_if_missing jq
brew_install_if_missing yq
brew_install_if_missing kubectl
brew_install_if_missing kind

# Docker is special: usually via Docker Desktop
if ! need docker; then
  echo "!! Docker is not installed or not in PATH."
  echo "   Install Docker Desktop for Mac, start it, then re-run this script."
  exit 1
fi

echo "==> Verifying Docker daemon..."
if ! docker info >/dev/null 2>&1; then
  echo "!! Docker daemon not reachable. Start Docker Desktop and try again."
  exit 1
fi

# --- Go env ------------------------------------------------------------------

echo "==> Configuring Go environment (temporary for this session)..."
export GOPATH="${GOPATH:-$GOPATH_DEFAULT}"
export PATH="${GOPATH}/bin:${PATH}"

echo "    GOPATH=${GOPATH}"

# --- Clone KubeVirt repo -----------------------------------------------------

if [ -d "${REPO_DIR}" ]; then
  echo "==> KubeVirt repo already exists at ${REPO_DIR}, pulling latest..."
  git -C "${REPO_DIR}" fetch --all --prune
  git -C "${REPO_DIR}" checkout main || true
  git -C "${REPO_DIR}" pull --ff-only || true
else
  echo "==> Cloning KubeVirt repo to ${REPO_DIR}..."
  mkdir -p "$(dirname "${REPO_DIR}")"
  git clone https://github.com/kubevirt/kubevirt.git "${REPO_DIR}"
fi

cd "${REPO_DIR}"

# --- Build (local dev binaries) ---------------------------------------------

echo "==> Running: make build"
make build

echo "==> (Optional) Running unit tests: make test"
if make test; then
  echo "==> Unit tests succeeded."
else
  echo "!! Some unit tests failed. You can investigate later; continuing with cluster setup..."
fi

# --- Create kind cluster -----------------------------------------------------

if kind get clusters | grep -q "^${CLUSTER_NAME}$"; then
  echo "==> kind cluster '${CLUSTER_NAME}' already exists."
else
  echo "==> Creating kind cluster '${CLUSTER_NAME}'..."
  kind create cluster --name "${CLUSTER_NAME}"
fi

# Point kubectl at this cluster
kubectl cluster-info --context "kind-${CLUSTER_NAME}" >/dev/null 2>&1 || {
  echo "!! Could not switch kubectl context to kind-${CLUSTER_NAME}"
  exit 1
}

# --- Install KubeVirt (stable release) --------------------------------------

echo "==> Fetching latest stable KubeVirt release version..."
RELEASE="$(curl -s https://storage.googleapis.com/kubevirt-prow/release/kubevirt/kubevirt/stable.txt)"

if [ -z "${RELEASE}" ]; then
  echo "!! Failed to get stable KubeVirt release version."
  exit 1
fi

echo "==> Using KubeVirt release: ${RELEASE}"

echo "==> Applying KubeVirt operator..."
kubectl apply -f "https://github.com/kubevirt/kubevirt/releases/download/${RELEASE}/kubevirt-operator.yaml"

echo "==> Applying KubeVirt CR..."
kubectl apply -f "https://github.com/kubevirt/kubevirt/releases/download/${RELEASE}/kubevirt-cr.yaml"

echo "==> Waiting for KubeVirt to become Available..."
kubectl -n kubevirt wait kv kubevirt --for condition=Available --timeout=5m

# --- Enable emulation mode ---------------------------------------------------

echo "==> Enabling emulation mode (no hardware KVM on macOS)..."
kubectl -n kubevirt patch kv kubevirt \
  --type merge \
  -p '{"spec": {"configuration": {"developerConfiguration": {"useEmulation": true}}}}' || {
    echo "!! Failed to patch KubeVirt for emulation. Check the kubevirt CR."
  }

# --- Install virtctl for macOS ----------------------------------------------

echo "==> Installing virtctl client for macOS..."

ARCH="$(uname -m)"
case "${ARCH}" in
  x86_64) ARCH="amd64" ;;
  arm64)  ARCH="arm64" ;;
esac

VIRTCTL_BIN="/usr/local/bin/virtctl"
TMP_VIRTCTL="$(mktemp)"

curl -L -o "${TMP_VIRTCTL}" \
  "https://github.com/kubevirt/kubevirt/releases/download/${RELEASE}/virtctl-${RELEASE}-darwin-${ARCH}"

chmod +x "${TMP_VIRTCTL}"
sudo mv "${TMP_VIRTCTL}" "${VIRTCTL_BIN}"

echo "==> virtctl installed to ${VIRTCTL_BIN}"

# --- Smoke test: create a tiny VM -------------------------------------------

echo "==> Applying sample VM manifest (cirros test VM)..."
kubectl apply -f https://kubevirt.io/labs/manifests/vm.yaml || {
  echo "!! Failed to apply sample VM, skipping smoke test."
  echo "   You can manually add workloads later."
  exit 0
}

echo "==> Listing VM objects..."
kubectl get vm

echo "==> You can now start the VM with:"
echo "    virtctl start testvm"
echo "    virtctl console testvm"

echo
echo "==> All done."
echo "   Repo:      ${REPO_DIR}"
echo "   Cluster:   kind-${CLUSTER_NAME}"
echo "   Emulation: enabled (useEmulation=true)"
echo
echo "Tip: Add this to your shell config for Go:"
echo "  export GOPATH=\"${GOPATH}\""

To use the script :

  • Save the script as myKubeVirt-Setup.sh
  • Make the file executable – chmod +x myKubeVirt-Setup.sh
  • Run the script

When it finishes, you’ll have:

  • A KubeVirt git checkout under ~/src/kubevirt
  • A working Go build + tests locally
  • A kind cluster named kubevirt-dev with KubeVirt installed
  • Emulation mode enabled so VMs can run without /dev/kvm
  • virtctl installed for managing VMs

Enjoy 👨🏻‍💻😃

I’m David

Welcome to my blog, my soapbox of opinionated views, learning experiences, what I’m working on and evangelism of causes I’m passionate about.

Let’s connect