CertGrid CertGrid
Hands-on Lab·Podman

Generating Kubernetes YAML from Podman

`podman kube generate` turns a pod you built by hand into a real `apiVersion: v1 / kind: Pod` manifest. The published port becomes a `hostPort`, which is the one field you should change before it goes near a cluster.

Pods Guide 13 of 47 Intermediate

Written against the versions above. Podman follows the distribution here rather than a vendor repository, so the version you get is the one Ubuntu shipped. The commands are stable across 5.x.

Every command on this page ran on podman01.
Server NameIP AddressOSRolesCPURAMHDD
PODMAN01192.168.0.24Ubuntu 26.04 LTSPrimary Container Host2 Core4 GB50 GB

Before you start

  1. Build something worth exporting

    Two containers with different images, and a published port, so the manifest has something to say. nginx as the storefront and redis as a cache - the shape of an application rather than two sleeps.

    bash Example session
    podman pod create --name shop -p 8090:804194fc9eb7f981215061cd01b98e7520a5161e2cb05ce7f2806519d0ea798ad9podman run -d --pod shop --name storefront docker.io/library/nginx:alpine25c4d3e13635bd045662db640176772de0398a3df3decf13c214b2ca3e11dfcfpodman run -d --pod shop --name cache docker.io/library/redis:alpineTrying to pull docker.io/library/redis:alpine...Getting image source signaturesCopying blob sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1Copying blob sha256:e6f31ffc071e5560b82a8685fba8214954e5721e3e49269d00958316edbe89feCopying blob sha256:0fa01b31bddd5bcc2f457623be6f36e4c5b895a832661f997bd99a200b9382c4Copying blob sha256:0fbf4c2f3ace70fd3b4d6279861e1d3ea5853bb6377323a02d0d5d0ed83b02cbCopying blob sha256:6e42bdae546bf5eeed860ec7de01b33a3b91916aa5cdde9d742808ed3a75ba61Copying blob sha256:12f303a814674bca7373b7a9eef56ff802850e99c93df05dc019e8eceada5cfbCopying blob sha256:41960ea7102b0b0dd3ad5abc29300a53a2ed7450089e1a0f087dd4450910813dCopying config sha256:00c30ddf0ef8074bbc7b7e5ea655bb6d359dc66694edd57d70fe95ce6ba531aaWriting manifest to image destination9bb2457ecddf5d356d96edd2798179880dfe20c5c5c176fba334394c0118fc1dpodman pod psPOD ID        NAME        STATUS      CREATED         INFRA ID      # OF CONTAINERS4194fc9eb7f9  shop        Running     15 seconds ago  2d5e8d85ede5  3

    Expected resultA running pod named shop with three containers.

    Success conditionTwo named containers and a published port exist in one pod.

  2. Export it

    podman kube generate shop writes the pod to stdout as Kubernetes YAML. It is a genuine manifest, not a Podman-shaped approximation - apiVersion: v1, kind: Pod, a spec.containers list.

    Read what it decided:

    • Container names lost the pod prefix. storefront, not shop-storefront. In a manifest the container name only has to be unique inside the pod.
    • The command became args. nginx's entrypoint was unpacked into a list, which is what a manifest needs since there is no shell to parse it.
    • The published port became hostPort: 8090 beside containerPort: 80.
    • Two io.kubernetes.cri-o.SandboxID annotations name the infra container. A cluster ignores them; they are Podman recording where this came from.

    The comment at the top tells you the intended use - kubectl create -f. That is the actual value of this command: a pod you debugged interactively becomes the first draft of a manifest, instead of you writing YAML from memory.

    bash Example session
    podman kube generate shop# Save the output of this file and use kubectl create -f to import# it into Kubernetes.## Created with podman-5.7.0apiVersion: v1kind: Podmetadata:  annotations:    io.kubernetes.cri-o.SandboxID/cache: 2d5e8d85ede5aa9e0de6841ad6efb3d95f3e0fd3f22da34ca66f4dc84d781695    io.kubernetes.cri-o.SandboxID/storefront: 2d5e8d85ede5aa9e0de6841ad6efb3d95f3e0fd3f22da34ca66f4dc84d781695  creationTimestamp: "2026-08-22T11:08:24Z"  labels:    app: shop  name: shopspec:  containers:  - args:    - nginx    - -g    - daemon off;    image: docker.io/library/nginx:alpine    name: storefront    ports:    - containerPort: 80      hostPort: 8090  - args:    - redis-server    image: docker.io/library/redis:alpine    name: cachepodman kube generate shop -f shop.yamlgrep -nE '^(apiVersion|kind|  name:|  - name:|    image:)' shop.yaml5:apiVersion: v16:kind: Pod14:  name: shop21:    image: docker.io/library/nginx:alpine28:    image: docker.io/library/redis:alpine

    Expected resultA kind: Pod manifest with two containers, and the same content written to shop.yaml.

    Success conditionYou have a manifest on disk describing the pod you built by hand.

  3. The one field to fix before a cluster sees it

    hostPort is the field to look at. It works, and Kubernetes discourages it: a pod that binds a port on its node can only be scheduled where that port is free, and one replica per node is the ceiling. In a cluster you would replace it with a Service.

    Podman had no choice - it is describing what you actually asked for, and what you asked for was a host port. This is the general shape of the translation: kube generate is accurate about your pod, not idiomatic about Kubernetes. Treat the output as a faithful first draft to edit, not a manifest to apply unread.

    Worth knowing what it does not emit, either: no Deployment, no Service, no resource requests you did not set, and no namespace. A bare Pod is rarely what you want in a cluster - but it is exactly what you had here.

    bash Example session
    head -30 shop.yaml# Save the output of this file and use kubectl create -f to import# it into Kubernetes.## Created with podman-5.7.0apiVersion: v1kind: Podmetadata:  annotations:    io.kubernetes.cri-o.SandboxID/cache: 2d5e8d85ede5aa9e0de6841ad6efb3d95f3e0fd3f22da34ca66f4dc84d781695    io.kubernetes.cri-o.SandboxID/storefront: 2d5e8d85ede5aa9e0de6841ad6efb3d95f3e0fd3f22da34ca66f4dc84d781695  creationTimestamp: "2026-08-22T11:08:25Z"  labels:    app: shop  name: shopspec:  containers:  - args:    - nginx    - -g    - daemon off;    image: docker.io/library/nginx:alpine    name: storefront    ports:    - containerPort: 80      hostPort: 8090  - args:

    Expected resultThe manifest, with hostPort: 8090 under the storefront container's ports.

    Success conditionYou can point at the field that needs changing and say why.

Troubleshooting

Official sources