Getting Started
This guide walks you through deploying Cisco Virtual Kubelet against a Kubernetes cluster and running your first pod on a Cisco IOS-XE device.
You'll install the Helm chart, which deploys a Kubernetes controller. The controller watches CiscoDevice custom resources and stands up a Virtual Kubelet pod for each one — this is the supported way to run Cisco Virtual Kubelet.
Prerequisites
| Requirement | Notes |
|---|---|
| Kubernetes cluster | v1.28+ recommended |
| Helm v3 | For installing the chart |
| Docker (or compatible) | For building the VK image locally |
| Container registry | Accessible from your cluster (Docker Hub, GHCR, internal, or sideload into KIND/minikube) |
| Cisco IOS-XE device | Catalyst 8000V (17.15.4c+), Catalyst 9000 (17.18.2+), IR1100 Series (17.12.4+), or IE3500 Series (17.18.2+) |
kubectl |
Configured to talk to your cluster |
On the device you need:
ioxconfigured — Cisco's on-device container runtime.restconfenabled on the HTTPS listener.- App-hosting support (standard on Catalyst 8000V and 9000).
- A signed container tar on device flash (e.g.
flash:/hello-app.iosxe.taron Cat8000V/Cat9000/IE3500, orbootflash:/hello-app.iosxe.taron IR1100) unless you explicitly allow unsigned packages.
See the platform-specific install guide for the exact IOS-XE CLI snippets: Catalyst 8000V, Catalyst 9000, IR1100 Series, or IE3500 Series.
1. Build and push the image
Note
The image has not been published to a public container registry yet, so it needs to be built locally and made available to your cluster.
Official releases are cut monthly. For a stable base, check out the latest release tag rather than main — main can contain unreleased in-flight changes.
git clone https://github.com/cisco-open/cisco-virtual-kubelet.git
cd cisco-virtual-kubelet
# Check out the latest release (recommended)
LATEST=$(git ls-remote --tags --refs --sort=-v:refname origin 'v*' | head -1 | awk '{print $2}' | sed 's|refs/tags/||')
git checkout "$LATEST"
# Build
docker build -t <your-registry>/cisco-vk:"$LATEST" .
# Push
docker push <your-registry>/cisco-vk:"$LATEST"
If your cluster is KIND or minikube you can side-load instead of pushing:
# KIND
kind load docker-image <your-registry>/cisco-vk:"$LATEST" --name <cluster>
# minikube
minikube image load <your-registry>/cisco-vk:"$LATEST"
2. Install the controller
The controller watches CiscoDevice custom resources and creates a Virtual Kubelet deployment for each one. The chart lives in-repo — point it at the image you just built.
helm install cvk ./charts/cisco-virtual-kubelet \
--namespace cvk-system --create-namespace \
--set image.repository=<your-registry>/cisco-vk \
--set image.tag="$LATEST"
The chart installs:
- The
CiscoDeviceCRD - The controller
Deployment(single replica; uses leader election for HA) - RBAC: controller service account with permissions on
ciscodevices, ConfigMaps, Deployments, and Nodes; VK service account with pod/node/events permissions
Both the controller pod and each VK pod it spawns use this image. To use different images for the two, set controllerImage.* and vkImage.* instead.
Verify:
If the controller pod is not Running, see Troubleshooting → CiscoDevice stuck in Provisioning.
3. Create a credential Secret
Device credentials are injected into the VK pod via a Kubernetes Secret — the controller never stores them in the ConfigMap. The Secret must be in the same namespace as the CiscoDevice and have a key named exactly password.
apiVersion: v1
kind: Secret
metadata:
name: cat9000-1-creds
namespace: default
type: Opaque
stringData:
password: <device-password>
Need multiple devices with different credentials? See Security → Managing credentials across multiple devices.
4. Create a CiscoDevice
The CiscoDevice CR describes how to reach the device and how containers should be networked on it.
apiVersion: cisco.vk/v1alpha1
kind: CiscoDevice
metadata:
name: cat9000-1
namespace: default
spec:
driver: XE
address: "192.168.1.100"
port: 443
username: admin
credentialSecretRef:
name: cat9000-1-creds # matches the Secret from step 3
tls:
enabled: true
insecureSkipVerify: true # acceptable for lab; use caFile in production
# allowUnsignedApps: true # uncomment for unsigned packages (own builds /
# test images). CVK disables device signing via
# RESTCONF and relaxes the reconciler guard.
xe:
networking:
interface:
type: VirtualPortGroup
virtualPortGroup:
dhcp: true
interface: "0"
guestInterface: 0
Within a few seconds you should see:
$ kubectl get ciscodevice
NAME DRIVER ADDRESS PHASE AGE
cat9000-1 XE 192.168.1.100 Ready 15s
$ kubectl get nodes
NAME STATUS ROLES AGE
cat9000-1 Ready agent 15s
If PHASE stays on Provisioning or shows Error, see Troubleshooting.
5. Deploy a pod
Schedule a pod directly onto the virtual node.
apiVersion: v1
kind: Pod
metadata:
name: hello-app
spec:
nodeName: cat9000-1
tolerations:
- key: virtual-kubelet.io/provider
operator: Exists
containers:
- name: hello
image: flash:/hello-app.iosxe.tar # path on device flash (use bootflash: on IR1100)
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
The image reference is a path on the device's flash storage, not a container registry — the container tar must already be on the device.
If the pod stays in Pending for more than a minute or ends up Failed, see Troubleshooting → Pod stuck in Pending or PackagePolicyInvalid false positives.
6. Verify on the device
You should see the container transition through DEPLOYED → ACTIVATED → RUNNING. Typical output of show app-hosting list:
What's next
- Configuration — Every field with defaults and validation rules
- Observability — Enable OpenTelemetry topology traces and scrape Prometheus metrics
- Security — TLS, credential rotation, and RBAC
- Troubleshooting — If anything above did not work