{"schemaVersion":"1.0","type":"TechArticle","types":["Article","TechArticle"],"slug":"running-vind-with-podman-local-kubernetes-without-docker-desktop-pwwwd","url":"https://zyvop.com/running-vind-with-podman-local-kubernetes-without-docker-desktop-pwwwd","title":"Running vinD with Podman: Local Kubernetes without Docker Desktop","subtitle":null,"tldr":"Transforming vinD to vinP (vCluster in Podman) on macOS! Introduction I always wanted to test vinD which seems more complete than Kind for a local test and depl...","keywords":["vind","Docker","podman","Kubernetes"],"entities":["Alain Airom (Ayrom)","Build Engineer","vind","Docker","podman","Kubernetes","ZyVOP"],"keyTakeaways":["Real Kubernetes Control Plane: Unlike standard namespaces, vcluster create provisions a fully isolated, dedicated Kubernetes control plane (API server, etcd, controller manager) inside one host container.","High-Speed Provisioning &amp; Resource Efficiency: Starts in ~15 seconds with a tiny RAM footprint (~150 MB). It supports sleep/wake capabilities (vcluster pause / vcluster resume) so an idle cluster uses zero CPU and zero memory.","Full Tenant Isolation: Multiple isolated vClusters can run side-by-side on a single host machine with separate CRDs, RBAC, and Kubernetes API versions without interfering with each other.","Native Tooling: You interact with vinD using standard kubectl, helm, and vcluster CLI commands just as you would with EKS, GKE, or Kind."],"headings":["TL;DR: What is vinD?","🎯 What is vind?","Why vind?","High-Level Architecture","Key Differences &amp; Podman Compatibility Shims","Application Source &amp; Multi-Stage Containerfile","Kubernetes Deployment Manifests","Orchestration &amp; Podman Shim Setup Script","Conclusion"],"outboundLinks":["https://github.com/loft-sh/vcluster","https://github.com/loft-sh/vind","https://github.com/podman-container-tools/podman","https://github.com/aairom/vinD-Podman","https://bob.ibm.com/"],"contentText":"Transforming vinD to vinP (vCluster in Podman) on macOS! Introduction I always wanted to test vinD which seems more complete than Kind for a local test and deployment. However the name is \"vind - vCluster in Docker\" and I use Podman... so I put IBM Bob into work to make all what it takes to use vinD with Podman locally. While vinD was explicitly designed as a Docker-native tool that expects standard Docker socket endpoints and CLI responses, migrating the setup to a macOS workstation running Podman Desktop is entirely feasible with a few targeted compatibility shims and VM adjustments. This post walks through the architectural considerations, step-by-step implementation, custom compatibility layers, and deployment manifests required to bring vinD up and running seamlessly on Podman. TL;DR: What is vinD? tl;dr — vinD (vCluster in Docker) gives you a real, lightweight Kubernetes cluster running inside a single container in seconds. Real Kubernetes Control Plane: Unlike standard namespaces, vcluster create provisions a fully isolated, dedicated Kubernetes control plane (API server, etcd, controller manager) inside one host container. High-Speed Provisioning &amp; Resource Efficiency: Starts in ~15 seconds with a tiny RAM footprint (~150 MB). It supports sleep/wake capabilities (vcluster pause / vcluster resume) so an idle cluster uses zero CPU and zero memory. Full Tenant Isolation: Multiple isolated vClusters can run side-by-side on a single host machine with separate CRDs, RBAC, and Kubernetes API versions without interfering with each other. Native Tooling: You interact with vinD using standard kubectl, helm, and vcluster CLI commands just as you would with EKS, GKE, or Kind. Excerpt from vinD Github; 🎯 What is vind? vind (vCluster in Docker) is an open-source way to run Kubernetes clusters directly as Docker containers. Built on top of vCluster, vind combines the power of virtual Kubernetes clusters with the simplicity of Docker, creating isolated Kubernetes environments that are perfect for development, testing, and CI/CD pipelines. Note: vind uses vCluster's Private Nodes mode internally. This is automatically enabled when using the Docker driver and is required for proper operation. This is expected behavior, not a configuration issue. Why vind? 🚀 Faster than KinD - Optimized container-based architecture 💤 Sleep &amp; Wake - Pause clusters to save resources, resume in under 3 seconds 🎨 Built-in UI - Free vCluster Platform UI for cluster management ⚡ Load Balancers OOB - Automatic LoadBalancer services without extra setup 🐳 Docker Native - Leverages Docker's networking and storage 🔄 Pull-through Cache - Faster image pulls via local Docker daemon 🌐 Hybrid Nodes - Join external nodes (even cloud instances) via VPN 📸 Snapshots - Save cluster state to OCI registries, S3, or local files and restore them 🔧 In-Place K8s Upgrades - Upgrade Kubernetes version without deleting the cluster Architecture Overview High-Level Architecture The following diagram illustrates how the macOS host environment interfaces with the underlying Podman VM and vinD control plane container: Key Differences &amp; Podman Compatibility Shims To bridge the gap between vcluster (which assumes a native Docker daemon) and Podman, four specific adjustments are required: Custom Docker-to-Podman CLI Shim: When probing network subnets, vcluster issues docker network inspect --format '{{.IPAM.Config}}'. Podman formats network inspect JSON under .subnets[].subnet. A lightweight shim intercepts this call and formats the JSON output accordingly. br_netfilter Kernel Module: Flannel CNI requires the br_netfilter module loaded in the Podman Machine VM. Loading this module explicitly prevents CNI initialization failure. DOCKER_HOST Socket Redirection: Standard API interactions route through Podman’s Docker-compatible API socket (unix:///.../podman.sock). Architecture-Aware Image Build (TARGETARCH): Multi-stage container builds must respect the Apple Silicon (arm64) execution environment of the Podman VM to avoid runtime exec format error issues. **Attention:* Installing vinD on macOS using Homebrew standard commands is not sufficient on its own*. Because the loft-sh/tap repository isn't trusted by default, I used **WailBrew* to explicitly flag and manage the tap as a trusted runnable source.* Implementation Application Source &amp; Multi-Stage Containerfile A lightweight HTTP service written in Go serves as the test workload. app/main.go // Package main provides a minimal HTTP Hello World server.// Port is controlled via the PORT environment variable (default: 8081).package main import ( \"fmt\" \"log\" \"net/http\" \"os\") // getPort returns the port to listen on.// It reads from the PORT environment variable; falls back to 8081.func getPort() string { port := os.Getenv(\"PORT\") if port == \"\" { port = \"8081\" } return port} // helloHandler writes \"Hello, World!\" to the response.func helloHandler(w http.ResponseWriter, r *http.Request) { log.Printf(\"Request received: %s %s from %s\", r.Method, r.URL.Path, r.RemoteAddr) fmt.Fprintln(w, \"Hello, World!\")} // healthHandler provides a basic liveness probe endpoint.func healthHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintln(w, \"OK\")} // newMux builds and returns the HTTP ServeMux with all routes registered.func newMux() *http.ServeMux { mux := http.NewServeMux() mux.HandleFunc(\"/\", helloHandler) mux.HandleFunc(\"/healthz\", healthHandler) return mux} func main() { port := getPort() addr := \":\" + port mux := newMux() log.Printf(\"Starting Hello World server on %s\", addr) if err := http.ListenAndServe(addr, mux); err != nil { log.Fatalf(\"Server failed: %v\", err) }} Enter fullscreen mode Exit fullscreen mode app/Containerfile # Stage 1 — BuilderFROM docker.io/library/golang:1.21-alpine AS builder WORKDIR /src COPY go.mod ./RUN go mod download COPY . .RUN go test ./... -v ARG TARGETARCHRUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH:-$(go env GOARCH)} \\ go build -ldflags=\"-s -w\" -o /out/helloworld . # Stage 2 — RuntimeFROM scratch COPY --from=builder /out/helloworld /helloworld EXPOSE 8081ENV PORT=8081USER 65534:65534 ENTRYPOINT [\"/helloworld\"] Enter fullscreen mode Exit fullscreen mode Kubernetes Deployment Manifests The service is exposed on NodePort 30080, which maps to the host. manifests/namespace.yaml apiVersion: v1kind: Namespacemetadata:name: helloworldlabels: app.kubernetes.io/managed-by: vind Enter fullscreen mode Exit fullscreen mode manifests/deployment.yaml apiVersion: apps/v1kind: Deploymentmetadata:name: helloworldnamespace: helloworldlabels: app: helloworld app.kubernetes.io/name: helloworld app.kubernetes.io/version: \"1.0.0\"spec:replicas: 1selector: matchLabels: app: helloworldtemplate: metadata: labels: app: helloworld spec: securityContext: runAsNonRoot: true runAsUser: 65534 runAsGroup: 65534 containers: - name: helloworld image: localhost/helloworld:latest imagePullPolicy: Never ports: - containerPort: 8081 protocol: TCP name: http env: - name: PORT value: \"8081\" resources: requests: cpu: \"50m\" memory: \"32Mi\" limits: cpu: \"200m\" memory: \"64Mi\" livenessProbe: httpGet: path: /healthz port: 8081 initialDelaySeconds: 5 periodSeconds: 10 readinessProbe: httpGet: path: /healthz port: 8081 initialDelaySeconds: 3 periodSeconds: 5 Enter fullscreen mode Exit fullscreen mode manifests/service.yaml apiVersion: v1kind: Servicemetadata:name: helloworldnamespace: helloworldlabels: app: helloworld app.kubernetes.io/name: helloworldspec:type: NodePortselector: app: helloworldports: - name: http port: 80 targetPort: 8081 nodePort: 30080 protocol: TCP Enter fullscreen mode Exit fullscreen mode and even a Load Balancer (manifests/loadbalancer-service.yaml) ---# =============================================================================# Kubernetes Service — helloworld (LoadBalancer type)## vinD provides automatic LoadBalancer support out-of-the-box.# The EXTERNAL-IP is assigned from the Podman/Docker bridge network.# Apply this INSTEAD of service.yaml if you prefer LoadBalancer over NodePort.## Apply with:# kubectl apply -f manifests/loadbalancer-service.yaml# =============================================================================apiVersion: v1kind: Servicemetadata: name: helloworld-lb namespace: helloworld labels: app: helloworld app.kubernetes.io/name: helloworldspec: type: LoadBalancer selector: app: helloworld ports: - name: http port: 80 # External port targetPort: 8081 # Container port protocol: TCP Enter fullscreen mode Exit fullscreen mode Orchestration &amp; Podman Shim Setup Script The orchestration script manages binary downloads, setting up the docker CLI translation shim, enabling br_netfilter in the Podman VM, creating the vCluster instance, importing images into containerd, and deploying manifests. scripts/setup.sh #!/usr/bin/env bashset -euo pipefail CLUSTER_NAME=\"${CLUSTER_NAME:-vind-helloworld}\"APP_IMAGE=\"${APP_IMAGE:-localhost/helloworld:latest}\" echo \"==&gt; 1. Ensuring vcluster CLI is installed...\"if ! command -v vcluster &amp;&gt; /dev/null; then mkdir -p \"$HOME/.local/bin\" curl -fsSL \"https://github.com/loft-sh/vcluster/releases/latest/download/vcluster-darwin-arm64\" \\ -o \"$HOME/.local/bin/vcluster\"chmod +x \"$HOME/.local/bin/vcluster\"export PATH=\"$HOME/.local/bin:$PATH\"fi echo \"==&gt; 2. Detecting Podman Socket...\"PODMAN_SOCK=$(podman info --format '{{.Host.RemoteSocket.Path}}' 2&gt;/dev/null || true)if [ -z \"$PODMAN_SOCK\" ]; then PODMAN_SOCK=\"$HOME/.local/share/containers/podman/machine/qemu/podman.sock\"fi export DOCKER_HOST=\"unix://${PODMAN_SOCK}\" echo \"==&gt; 3. Creating docker -&gt; podman CLI shim with IPAM translation...\"mkdir -p \"$HOME/.local/bin\"cat &lt;&lt; 'EOF' &gt; \"$HOME/.local/bin/docker\" #!/usr/bin/env bash if [[ \"$*\" == *\"network inspect\"* ]] &amp;&amp; [[ \"$*\" == *\"IPAM\"* ]]; then NET_NAME=\"${@:$#}\" SUBNET=$(podman network inspect \"$NET_NAME\" --format '{{range .subnets}}{{.subnet}}{{end}}' 2&gt;/dev/null) echo \"[{\\\"Subnet\\\":\\\"${SUBNET}\\\"}]\" exit 0 fi exec podman \"$@\" EOF chmod +x \"$HOME/.local/bin/docker\" export PATH=\"$HOME/.local/bin:$PATH\" echo \"==&gt; 3b. Loading br_netfilter module in Podman VM...\" podman machine ssh \"sudo modprobe br_netfilter\" || true podman machine ssh \"echo 'br_netfilter' | sudo tee /etc/modules-load.d/br_netfilter.conf\" || true echo \"==&gt; 4. Configuring vCluster driver...\" vcluster use driver docker echo \"==&gt; 5. Creating vinD cluster...\" if ! vcluster list | grep -q \"$CLUSTER_NAME\"; then vcluster create \"$CLUSTER_NAME\" -f vcluster.yaml fi echo \"==&gt; 6. Building Go application image...\" podman build -t \"$APP_IMAGE\" -f app/Containerfile app/ echo \"==&gt; 7. Importing image into cluster containerd...\" podman save \"$APP_IMAGE\" | docker exec -i \"vcluster.cp.${CLUSTER_NAME}\" ctr --namespace k8s.io images import - echo \"==&gt; 8. Applying Kubernetes manifests...\" kubectl apply -f manifests/namespace.yaml kubectl apply -f manifests/deployment.yaml kubectl apply -f manifests/service.yaml echo \"==&gt; 9. Waiting for deployment readiness...\" kubectl rollout status deployment/helloworld -n helloworld --timeout=60s echo \"==&gt; Setup complete! Test endpoint:\" echo \"curl http://localhost:30080\" Enter fullscreen mode Exit fullscreen mode Conclusion By addressing the four core points of divergence between Docker and Podman environments—CLI output formatting, kernel module presence in the underlying machine VM, platform-native image builds, and API socket location—vinD runs efficiently on top of Podman. This setup offers a fast local deployment pipeline, retaining vCluster's single-container control plane model while operating fully within open-source, Podman-based development tooling. Thanks for reading 🍇 Links vinD: https://github.com/loft-sh/vind Podman: https://github.com/podman-container-tools/podman Code repo for this post: https://github.com/aairom/vinD-Podman IBM Bob: https://bob.ibm.com/","contentHash":"sha256:fa723dbd234fe4aee1c6ff3315fed2c998b269af3c164bc496894583b5cd0649","authorName":"Alain Airom (Ayrom)","authorUrl":"https://zyvop.com/author/alain","authorSameAs":["https://github.com/aairom","https://www.linkedin.com/in/aairom/"],"category":null,"tags":["vind","Docker","podman","Kubernetes"],"audience":"Senior software engineers, systems architects, and technical leads working with vind","tone":"Professional, build engineer perspective","readingTimeMinutes":7,"wordCount":1589,"faqs":null,"primaryTopic":"vind","publishedAt":"2026-09-03T11:11:52.898Z","updatedAt":"2026-09-03T11:11:52.898Z","canonicalUrl":"https://dev.to/aairom/running-vind-with-podman-local-kubernetes-without-docker-desktop-1gc6"}