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
- OSUbuntu 26.04 LTS (resolute)
- Podman5.7.0
- Runtimecrun 1.21
- Networknetavark 1.16.1
- TimeAbout 14 min
- Reviewed22 August 2026
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.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| PODMAN01 | 192.168.0.24 | Ubuntu 26.04 LTS | Primary Container Host | 2 Core | 4 GB | 50 GB |
Before you start
-
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 3Expected resultA running pod named
shopwith three containers.Success conditionTwo named containers and a published port exist in one pod.
-
Export it
podman kube generate shopwrites the pod to stdout as Kubernetes YAML. It is a genuine manifest, not a Podman-shaped approximation -apiVersion: v1,kind: Pod, aspec.containerslist.Read what it decided:
- Container names lost the pod prefix.
storefront, notshop-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: 8090besidecontainerPort: 80. - Two
io.kubernetes.cri-o.SandboxIDannotations 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:alpineExpected resultA
kind: Podmanifest with two containers, and the same content written toshop.yaml.Success conditionYou have a manifest on disk describing the pod you built by hand.
- Container names lost the pod prefix.
-
The one field to fix before a cluster sees it
hostPortis 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 generateis 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: 8090under the storefront container's ports.Success conditionYou can point at the field that needs changing and say why.
Troubleshooting
Error: pod shop not found.Why:
kube generatetakes a pod or container name, and the pod may have been removed or created under a different name.Fix:
podman pod psfor the exact name. It also accepts container names, in which case it wraps the single container in a generated pod.The generated YAML contains
creationTimestampand SandboxID annotations you do not want in version control.Why: It is a faithful dump of live state, including when that state was created.
Fix:They are inert - a cluster ignores unknown annotations and overwrites the timestamp. Delete them by hand if the file is going into a repository.
You wanted a Deployment, not a Pod.
Why:
kube generatedescribes what exists, and what exists is a pod. Podman has no Deployment to export.Fix:
podman kube generate --type deployment shopwraps the pod spec in a Deployment. It is a template withreplicas: 1- the replica count is the subject of guide 24.