| Tested on | Red Hat Enterprise Linux 10.2 (Coughlan) |
|---|---|
| Package | podman-5.8.2-5.el10_2.x86_64 |
| Applies to | Linux hosts with Podman where you want Kubernetes-style YAML from existing containers, pods, or volumes |
| Privilege | Normal user for rootless generation; root when source objects or bind mounts require it |
| Scope | podman kube generate from containers, pods, and volumes, --filename, --type (Pod, Deployment, DaemonSet, Job), --replicas, --service, --podman-only, bind-mount to hostPath and named-volume to PVC mapping, multi-document YAML, generate-to-kube play round trip and what changes, production-manifest review warnings, comparison to podman generate spec, and when to use each export format. Does not cover running YAML (kube play tutorial), kubectl apply, Kubernetes manifest design, or Quadlet generation. |
| Related guides | Podman volumes Manage Podman secrets Podman vs Docker |
You already have a working Podman container or pod and want Kubernetes-style YAML — to replay locally with podman kube play or as a draft for a real cluster. podman kube generate walks the other direction from kube play: it reads local Podman state and emits YAML.
That output is a translation, not a production manifest generator. Treat every file as a starting artifact you must review before kubectl apply or handoff to another team.
What podman kube generate does
Podman container / pod / volume
│
▼
podman kube generate
│
▼
Kubernetes-style YAMLWhether the input is a standalone container or a pod, Podman typically emits a Pod document (or a higher-level kind when you pass --type). The YAML can round-trip through kube play on the same host, but it is not a lossless copy of every Podman CLI flag.
Generate YAML from a container
Start a container with a port mapping and environment variable:
podman run -d --name gen-web -p 8080:80 -e APP_ENV=production docker.io/library/nginx:latestExport it to stdout:
podman kube generate gen-webSample output (trimmed — proxy variables from the host environment omitted):
apiVersion: v1
kind: Pod
metadata:
labels:
app: gen-web-pod
name: gen-web-pod
spec:
containers:
- args:
- nginx
- -g
- daemon off;
env:
- name: APP_ENV
value: production
image: docker.io/library/nginx:latest
name: gen-web
ports:
- containerPort: 80
hostPort: 8080Standalone container gen-web becomes pod metadata gen-web-pod. Inherited host environment variables may appear in the YAML — trim them before publishing manifests.
Write generated YAML to a file
Redirect output with --filename (or -f):
podman kube generate --filename web.yaml gen-webThe command exits silently on success. Running again against the same path refuses to overwrite:
podman kube generate --filename web.yaml gen-webSample output:
Error: cannot write to "web.yaml"; file existsThat guard prevents accidental clobbering of a manifest you already edited.
Generate YAML from a Podman pod
Multi-container pods export both containers in one document. Create a pod with nginx and a sidecar:
podman pod create --name gen-pod -p 19090:80Add the main nginx container to that pod:
podman run -d --pod gen-pod --name gen-pod-nginx docker.io/library/nginx:latestAdd a sidecar that stays running while you export YAML:
podman run -d --pod gen-pod --name gen-pod-sidecar docker.io/library/alpine:latest sleep 3600Generate from the pod name:
podman kube generate gen-podSample output (trimmed):
apiVersion: v1
kind: Pod
metadata:
name: gen-pod
spec:
containers:
- image: docker.io/library/nginx:latest
name: gen-pod-nginx
ports:
- containerPort: 80
hostPort: 19090
- command:
- sleep
- "3600"
image: docker.io/library/alpine:latest
name: gen-pod-sidecarCompared with a single-container export, you get explicit sidecar command, shared pod networking, and per-container names preserved in one Pod spec.
Generate from multiple objects
kube generate accepts several containers, pods, or volumes in one invocation. Create a named volume and attach it:
podman volume create gen-dataAttach the volume when you start the application container:
podman run -d --name gen-app -v gen-data:/data docker.io/library/nginx:latestExport container and volume together:
podman kube generate gen-app gen-dataSample output (trimmed, showing document separator):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
annotations:
volume.podman.io/driver: local
name: gen-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
name: gen-app-pod
spec:
containers:
- name: gen-app
volumeMounts:
- mountPath: /data
name: gen-data-pvc
volumes:
- name: gen-data-pvc
persistentVolumeClaim:
claimName: gen-dataMulti-document YAML uses --- between resources. Podman may insert comment blocks about SELinux volume permissions between documents on enforcing hosts.
Generate a Kubernetes Deployment
Higher-level kinds come from --type:
podman kube generate --type deployment gen-webDefault replica count is one when omitted. Request three replicas explicitly:
podman kube generate --type deployment --replicas 3 gen-webSample output (trimmed):
apiVersion: apps/v1
kind: Deployment
metadata:
name: gen-web-pod-deployment
spec:
replicas: 3
selector:
matchLabels:
app: gen-web-pod
template:
spec:
containers:
- name: gen-web
image: docker.io/library/nginx:latestGenerator output and runtime behavior diverge here. --replicas 3 writes the field into YAML, but podman kube play on Podman 5.8.2 does not run a Deployment controller and limits local replicas to one. Applying the same file to a real Kubernetes cluster can scale normally after you review storage, images, and probes.
Generate a DaemonSet
Export the same container as a DaemonSet template:
podman kube generate --type daemonset gen-webSample output (trimmed):
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: gen-web-pod-daemonset
spec:
selector:
matchLabels:
app: gen-web-pod
template:
spec:
containers:
- name: gen-webThe YAML kind is useful as a cluster draft. Local kube play cannot reproduce one pod per node because there is no scheduler or node inventory on a single Podman host.
Generate a Job
Job export uses the batch API shape:
podman kube generate --type job gen-webSample output (trimmed):
apiVersion: batch/v1
kind: Job
metadata:
name: gen-web-pod-job
spec:
completions: 1
parallelism: 1
template:
spec:
containers:
- name: gen-web
restartPolicy: NeverGenerated Jobs default to restartPolicy: Never in the pod template unless Job semantics require otherwise. This section stops at export shape — Kubernetes Job lifecycle teaching belongs elsewhere.
Generate a Kubernetes Service
When the source publishes ports, add a Service document:
podman kube generate --service gen-webSample output (trimmed):
apiVersion: v1
kind: Service
metadata:
name: gen-web-pod
spec:
ports:
- name: "80"
nodePort: 32038
port: 80
targetPort: 80
selector:
app: gen-web-pod
type: NodePort
---
apiVersion: v1
kind: Pod
...Podman assigns a random nodePort in the NodePort range (here 32038). For a real cluster, review Service type, port names, targetPort, and selectors — do not treat generated NodePort YAML as production-ready without edits.
How bind mounts become hostPath
Bind mount -v /srv/web:/data maps to Kubernetes hostPath in generated YAML.
Prepare a host directory and run a container:
mkdir -p /tmp/gen-bindRun nginx with a bind mount into that directory:
podman run -d --name gen-bind -v /tmp/gen-bind:/data:Z docker.io/library/nginx:latestGenerate and inspect volume stanzas:
podman kube generate gen-bindSample output (trimmed):
metadata:
annotations:
bind-mount-options: /tmp/gen-bind:Z
spec:
volumes:
- hostPath:
path: /tmp/gen-bind
type: Directory
name: tmp-gen-bind-host-0When the host path exists as a directory, type is typically Directory. Paths that do not exist yet may generate DirectoryOrCreate depending on source state. SELinux mount options can appear in bind-mount-options annotations.
hostPath ties the workload to a specific node filesystem. That is often wrong for portable multi-node Kubernetes — replace with appropriate storage classes or shared volumes during cluster review.
How named volumes become PVCs
Named Podman volume gen-data becomes a PersistentVolumeClaim with claimName: gen-data in the pod spec, as shown in the multi-object example above.
The annotation volume.podman.io/driver: local records the Podman driver. Generated PVCs do not create a Kubernetes storage backend, StorageClass, or dynamic provisioner — they are structural translations you must map to real cluster storage.
Generate YAML from a Podman volume alone
Export only the volume:
podman kube generate gen-dataSample output:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
annotations:
volume.podman.io/driver: local
name: gen-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1GiUseful when you want a PVC document to pair with a separately generated pod, or to snapshot volume metadata before a round trip.
The --podman-only flag
Request Podman-reserved annotations on export:
podman kube generate --podman-only gen-podDocumentation describes reserved annotations that improve round-trip fidelity with kube play. On Podman 5.8.2 in this lab, output matched the default generate for tested pods — CRI sandbox ID and bind-mount annotations appeared without the flag.
--podman-only adds Podman-specific reserved annotations for kube play fidelity. Podman documents YAML generated with this option as not usable directly by Kubernetes; remove the Podman-only annotations before using the manifest with kubectl.
Round trip: container → YAML → Podman
This is the practical fidelity test.
Create a container with env, port, and volume:
podman volume create roundtrip-volStart the source container with env, port, and volume mount:
podman run -d --name roundtrip-ctr -p 18080:80 -e ROUNDTRIP_ENV=hello -v roundtrip-vol:/data docker.io/library/nginx:latestWrite YAML and remove the original:
podman kube generate --filename roundtrip.yaml --podman-only roundtrip-ctrStop and delete the standalone container so play must recreate it from YAML:
podman stop roundtrip-ctr && podman rm roundtrip-ctrReplay through kube play:
podman kube play roundtrip.yamlSample output:
Pod:
f038dafcc4e027af6aef116d75b608e3737cc6f69ec6d841889351be5160581e
Container:
c50d163e24d1aa4724f7d7acc5e5ef4bc017139ab3d4dff753d2c2614b45d359Compare what survived:
| Field | Original roundtrip-ctr |
After kube play |
|---|---|---|
| Image | docker.io/library/nginx:latest |
Same |
| Custom env | ROUNDTRIP_ENV=hello |
Same |
Named volume /data |
roundtrip-vol |
Same claim name |
| Container name | roundtrip-ctr |
roundtrip-ctr-pod-roundtrip-ctr (pod wrapper) |
Host port 18080 |
On container port bindings | Published via pod infra; workload HostConfig.PortBindings empty |
Image, custom environment, volume mount, and host port survived. Object names and port-binding structure changed because kube play always creates a pod with an infra container. Do not assume bit-for-bit reversibility.
Tear down when finished:
podman kube down roundtrip.yamlWhat podman kube generate loses or changes
Only document differences you can reproduce. On Podman 5.8.2 this lab observed:
- Standalone containers rewrapped as pods with different runtime names
- Host port publishing moved to pod networking after round trip
- Inherited host proxy environment copied into YAML unless you filter it
- Podman network topology, Quadlet units, and systemd lifecycle not exported
- Secrets and health/readiness semantics not fully represented
- SELinux bind-mount options preserved as annotations, not portable cluster policy
If a field matters for your app, inspect generated YAML and the replayed podman inspect output — do not trust the generate step alone.
Generated YAML is not production Kubernetes YAML
Review before any cluster apply.
Images
Local tags such as localhost/... are invisible to other nodes. Push to a registry and pin digests.
hostPath
Replace node-specific paths with shared storage where the workload must move between nodes.
Secrets
Do not ship literal test secret values from lab exports. Use Kubernetes Secret objects and your cluster secret workflow.
Services
Replace auto-assigned NodePort with ClusterIP, LoadBalancer, or Ingress patterns your platform expects.
Resource requests and limits
Generated YAML often omits CPU and memory requests. Add cluster-appropriate values.
SecurityContext
Review UID/GID, capabilities, privilege, read-only root filesystem, and SELinux settings against policy.
Probes
Generation and kube play support does not cover every readiness or startup pattern Kubernetes controllers expect.
Deployment characteristics
Review replica counts, rollout strategy, and selectors separately for generator output versus kube play runtime limits.
podman kube generate vs podman kube play
| Command | Direction |
|---|---|
podman kube generate |
Podman objects → Kubernetes YAML |
podman kube play |
Kubernetes YAML → Podman objects |
Round-trip mental model:
Podman objects
↓ kube generate
YAML
↓ kube play
Podman objectsPlaying YAML is documented in Run Kubernetes YAML with podman kube play. This page owns the export direction and review checklist.
podman kube generate vs podman generate spec
podman generate spec exports Podman SpecGen JSON — the internal representation for creating containers through the Podman API:
podman generate spec --filename spec.json gen-webSample output (trimmed):
{
"name": "gen-web-clone",
"image": "docker.io/library/nginx:latest",
"command": ["nginx", "-g", "daemon off;"],
"env": {
"APP_ENV": "production"
},
"portmappings": [
{"host_port": 8080, "container_port": 80, "protocol": "tcp"}
]
}| Export | Format | Best for |
|---|---|---|
podman kube generate |
Kubernetes YAML | kube play, Kubernetes draft manifests |
podman generate spec |
SpecGen JSON | Podman API automation, higher Podman field fidelity |
SpecGen preserves netns, userns, and containerCreateCommand details that have no Kubernetes YAML equivalent. It is not a drop-in replacement for Quadlet or for portable cluster manifests.
When to use which export format
| Goal | Use |
|---|---|
| Starting manifest for a Kubernetes cluster | podman kube generate, then manual review |
| Retain Podman-native API representation | podman generate spec |
| Persistent service declaration on boot | Podman Quadlet |
| Move image between hosts | Registry push/pull or podman save / podman load |
| Move application data | Volume backup — not YAML alone |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
cannot write to "file.yaml"; file exists |
Destination already present | Use a new filename or remove the old file intentionally |
| Unexpected proxy env in YAML | Host environment inherited into container | Filter env before generate or edit YAML |
| Deployment replicas ignored on replay | kube play local replica cap |
Expected on 5.8.2 — test scaling on a real cluster |
| Round-trip container renamed | Pod wrapper from kube play |
Match on labels or pod name, not original container name |
| PVC play fails on SELinux host | Volume permissions | Follow man-page SELinux steps; see Podman volume permissions |
generate spec name ends with -clone |
Default --name behavior |
Pass naming options or edit JSON |
References
- podman-kube-generate(1) — flags, kinds, and volume mapping
- podman-generate-spec(1) — SpecGen JSON export
- Kubernetes object specifications — upstream field meanings for review
- Red Hat — Building, running, and managing containers — kube generate on RHEL-family systems
Summary
podman kube generate turns local Podman containers, pods, and volumes into Kubernetes-style YAML you can replay with kube play or edit for a cluster. Standalone containers become pod documents; bind mounts become hostPath; named volumes become PVC references. Flags add Deployments, DaemonSets, Jobs, Services, and multi-document output.
Generator capability is not the same as local runtime capability. A Deployment with replicas: 3 is valid YAML, but Podman kube play on 5.8.2 still runs one local pod. Round-trip testing showed image, environment, volume, and port data surviving while container names and port-binding structure changed.
For Podman-native automation, podman generate spec exports SpecGen JSON with higher fidelity to Podman internals. For boot-time services, Quadlet remains the declarative path. Review every generated file for images, storage, secrets, Services, and security before treating it as production Kubernetes YAML.

