Generate Kubernetes YAML from Podman Containers with `podman kube generate`

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

text
Podman container / pod / volume
podman kube generate
Kubernetes-style YAML

Whether 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:

bash
podman run -d --name gen-web -p 8080:80 -e APP_ENV=production docker.io/library/nginx:latest

Export it to stdout:

bash
podman kube generate gen-web

Sample output (trimmed — proxy variables from the host environment omitted):

output
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: 8080

Standalone 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):

bash
podman kube generate --filename web.yaml gen-web

The command exits silently on success. Running again against the same path refuses to overwrite:

bash
podman kube generate --filename web.yaml gen-web

Sample output:

output
Error: cannot write to "web.yaml"; file exists

That 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:

bash
podman pod create --name gen-pod -p 19090:80

Add the main nginx container to that pod:

bash
podman run -d --pod gen-pod --name gen-pod-nginx docker.io/library/nginx:latest

Add a sidecar that stays running while you export YAML:

bash
podman run -d --pod gen-pod --name gen-pod-sidecar docker.io/library/alpine:latest sleep 3600

Generate from the pod name:

bash
podman kube generate gen-pod

Sample output (trimmed):

output
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-sidecar

Compared 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:

bash
podman volume create gen-data

Attach the volume when you start the application container:

bash
podman run -d --name gen-app -v gen-data:/data docker.io/library/nginx:latest

Export container and volume together:

bash
podman kube generate gen-app gen-data

Sample output (trimmed, showing document separator):

output
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-data

Multi-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:

bash
podman kube generate --type deployment gen-web

Default replica count is one when omitted. Request three replicas explicitly:

bash
podman kube generate --type deployment --replicas 3 gen-web

Sample output (trimmed):

output
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:latest

Generator 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:

bash
podman kube generate --type daemonset gen-web

Sample output (trimmed):

output
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: gen-web-pod-daemonset
spec:
  selector:
    matchLabels:
      app: gen-web-pod
  template:
    spec:
      containers:
      - name: gen-web

The 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:

bash
podman kube generate --type job gen-web

Sample output (trimmed):

output
apiVersion: batch/v1
kind: Job
metadata:
  name: gen-web-pod-job
spec:
  completions: 1
  parallelism: 1
  template:
    spec:
      containers:
      - name: gen-web
      restartPolicy: Never

Generated 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:

bash
podman kube generate --service gen-web

Sample output (trimmed):

output
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:

bash
mkdir -p /tmp/gen-bind

Run nginx with a bind mount into that directory:

bash
podman run -d --name gen-bind -v /tmp/gen-bind:/data:Z docker.io/library/nginx:latest

Generate and inspect volume stanzas:

bash
podman kube generate gen-bind

Sample output (trimmed):

output
metadata:
  annotations:
    bind-mount-options: /tmp/gen-bind:Z
spec:
  volumes:
  - hostPath:
      path: /tmp/gen-bind
      type: Directory
    name: tmp-gen-bind-host-0

When 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:

bash
podman kube generate gen-data

Sample output:

output
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  annotations:
    volume.podman.io/driver: local
  name: gen-data
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

Useful 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:

bash
podman kube generate --podman-only gen-pod

Documentation 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:

bash
podman volume create roundtrip-vol

Start the source container with env, port, and volume mount:

bash
podman run -d --name roundtrip-ctr -p 18080:80 -e ROUNDTRIP_ENV=hello -v roundtrip-vol:/data docker.io/library/nginx:latest

Write YAML and remove the original:

bash
podman kube generate --filename roundtrip.yaml --podman-only roundtrip-ctr

Stop and delete the standalone container so play must recreate it from YAML:

bash
podman stop roundtrip-ctr && podman rm roundtrip-ctr

Replay through kube play:

bash
podman kube play roundtrip.yaml

Sample output:

output
Pod:
f038dafcc4e027af6aef116d75b608e3737cc6f69ec6d841889351be5160581e
Container:
c50d163e24d1aa4724f7d7acc5e5ef4bc017139ab3d4dff753d2c2614b45d359

Compare 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:

bash
podman kube down roundtrip.yaml

What 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:

text
Podman objects
      ↓ kube generate
YAML
      ↓ kube play
Podman objects

Playing 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:

bash
podman generate spec --filename spec.json gen-web

Sample output (trimmed):

json
{
  "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


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.


Frequently Asked Questions

1. What is the difference between podman kube generate and podman kube play?

podman kube generate exports local Podman containers, pods, or volumes into Kubernetes-style YAML. podman kube play imports that YAML back into Podman objects. Generate is Podman to YAML; play is YAML to Podman.

2. Does podman kube generate create production-ready Kubernetes manifests?

No. Output is a translation of local Podman state. Review images, hostPath mounts, Services, secrets, securityContext, resource limits, and probes before applying YAML to a real cluster.

3. Can I generate a Deployment with three replicas and replay it locally with kube play?

You can generate replicas 3 in the YAML, but Podman kube play does not run a Deployment controller and caps local replicas to one on Podman 5.8.2. A real Kubernetes cluster can honor the replica count after you review the manifest.

4. What is the difference between podman kube generate and podman generate spec?

kube generate writes Kubernetes YAML for kube play or as a cluster starting point. generate spec writes Podman SpecGen JSON for Podman API workflows and preserves more Podman-native fields that have no Kubernetes equivalent.

5. Will podman kube generate overwrite an existing YAML file?

No. When --filename points at an existing file, Podman exits with an error instead of silently replacing the destination.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)