`podman update`: Change an Existing Container

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 need to change resource limits, health probes, environment, ulimits, or restart policy on an existing container without deleting its object
Privilege Rootful examples on the lab host; rootless cgroup v2 and systemd delegation caveats noted where limits fail
Scope podman update for CPU, memory, swap, PIDs, cpuset, block I/O flags, health-check options, --env and --unsetenv, --ulimit, --restart, immediate versus restart behavior, cgroup v2 verification, unsupported fields, podman container clone as fallback, and update versus clone versus recreate. Does not cover full resource-limit theory, health-check probe design, Quadlet editing, or generic image update workflows.
Related guides Inspect Podman containers
Run containers with podman run
Podman Quadlet container file

podman update changes configuration on an existing container object. You keep the same container ID, log history, and name while adjusting resource limits, health probes, environment metadata, ulimits, or restart policy that podman run would have set at create time.

Not every field is updatable. A different image can be supplied with podman container clone, while changing published ports, bind mounts, the container command, or most namespace settings requires recreating the container with podman run or its declarative equivalent. This guide maps what works on Podman 5.8.2, what applies immediately, and what waits for the next container start.


What can podman update change?

Verified on Podman 5.8.2:

Configuration Update container object? Active immediately?
CPU quota (--cpus, --cpu-quota, --cpu-period) Yes Yes — cgroup cpu.max changes while running
Memory limits (--memory, --memory-swap) Yes Yes — cgroup memory.max changes while running
PID limit (--pids-limit) Yes Yes — cgroup pids.max changes while running
CPU affinity (--cpuset-cpus) Yes Yes — cgroup cpuset.cpus changes while running
Block I/O weight and per-device rates Yes Yes — cgroup blkio fields update
Health-check configuration Yes Applies to probe scheduling on the existing container
Restart policy (--restart) Yes New policy is stored immediately and applies to subsequent container exits
Environment (--env, --unsetenv) Yes Inspect and new exec sessions see changes; PID 1 keeps old environ until restart
Ulimits (--ulimit) Yes Inspect updates immediately; PID 1 limits apply after restart
Image No Use podman container clone or recreate
Port mappings No Cannot be changed by update; clone inherits existing mappings but 5.8.2 cannot override them — recreate to change
Bind mounts and volumes No Cannot be changed by update; clone can inherit them but not redefine them — recreate to change
Network topology No Network commands or recreate depending on change
User namespace mode No Recreate

For limit semantics and rootless delegation errors, see Set container resource limits. For probe behavior after health changes, see Podman health checks.


Lab container

Create a long-running container with starting limits and environment:

bash
podman run -d --name upd-demo --cpus 1 --memory 256m --pids-limit 100 --env APP_MODE=old --ulimit nofile=1024:1024 registry.access.redhat.com/ubi9/ubi-minimal:latest sleep 3600

The lab uses UBI minimal because Docker Hub rate limits blocked alpine:latest on this host. Any small image with a long-running command works.

Baseline inspect values:

bash
podman inspect --format 'NanoCpus={{.HostConfig.NanoCpus}} Memory={{.HostConfig.Memory}} Pids={{.HostConfig.PidsLimit}}' upd-demo

Sample output:

output
NanoCpus=1000000000 Memory=268435456 Pids=100

Update CPU limit

Lower the CPU cap without recreating the container:

bash
podman update --cpus 0.5 upd-demo

Podman prints the container ID on success. Confirm the stored quota:

bash
podman inspect --format '{{.HostConfig.NanoCpus}}' upd-demo

Sample output:

output
500000000

500000000 nanoseconds of CPU per second is 0.5 CPU. Read the cgroup file the kernel enforces:

bash
cat "/sys/fs/cgroup$(cut -d: -f3 /proc/$(podman inspect --format '{{.State.Pid}}' upd-demo)/cgroup)/cpu.max"

Sample output:

output
50000 100000

cpu.max matching inspect proves the running container picked up the new limit immediately.


Update memory limit

Raise the memory ceiling:

bash
podman update --memory 512m upd-demo

Verify bytes in inspect:

bash
podman inspect --format '{{.HostConfig.Memory}}' upd-demo

Sample output:

output
536870912

Read memory.max from the container cgroup:

bash
cat "/sys/fs/cgroup$(cut -d: -f3 /proc/$(podman inspect --format '{{.State.Pid}}' upd-demo)/cgroup)/memory.max"

Sample output:

output
536870912

Shrinking memory below current resident usage can fail or behave differently depending on cgroup pressure — test downward changes on disposable containers. Upward changes on an idle sleep container applied cleanly in the lab.


Update memory and swap together

Set combined memory and swap ceilings in one command:

bash
podman update --memory 512m --memory-swap 1g upd-demo

Inspect stores both totals in bytes:

bash
podman inspect --format '{{.HostConfig.Memory}} {{.HostConfig.MemorySwap}}' upd-demo

Sample output:

output
536870912 1073741824

Combined swap semantics are documented in Set container resource limits — this section only confirms podman update accepts the pair on an existing container.


Update PID limit

Raise the process cap:

bash
podman update --pids-limit 200 upd-demo

Confirm in inspect and cgroup:

bash
podman inspect --format '{{.HostConfig.PidsLimit}}' upd-demo

Sample output:

output
200

The cgroup pids.max file should match:

bash
cat "/sys/fs/cgroup$(cut -d: -f3 /proc/$(podman inspect --format '{{.State.Pid}}' upd-demo)/cgroup)/pids.max"

Sample output:

output
200

Update CPU affinity

Pin the container to one CPU:

bash
podman update --cpuset-cpus 0 upd-demo

Inspect and cgroup should agree:

bash
podman inspect --format '{{.HostConfig.CpusetCpus}}' upd-demo

Sample output:

output
0

Read cpuset.cpus under the same cgroup path:

bash
cat "/sys/fs/cgroup$(cut -d: -f3 /proc/$(podman inspect --format '{{.State.Pid}}' upd-demo)/cgroup)/cpuset.cpus"

Sample output:

output
0

On a single-NUMA host, --cpuset-mems is rarely needed. Multi-NUMA pinning follows the same inspect-and-cgroup pattern from the limits guide.


Update block I/O limits

podman update can also change block-device weights and per-device throughput or IOPS limits on an existing container.

Limit reads from a block device:

bash
podman update --device-read-bps /dev/sda:10mb upd-demo

Or change relative block I/O weight:

bash
podman update --blkio-weight 500 upd-demo

Podman 5.8.2 also supports --blkio-weight-device, --device-read-iops, --device-write-bps, and --device-write-iops on update. These options depend on the host cgroup version, controller availability, and storage/device layout. Rootless cgroup v1 does not support them. Use the resource-limits guide for block I/O semantics and controller troubleshooting.


Update health-check configuration

Start a container with a probe:

bash
podman run -d --name upd-health --health-cmd "test -f /tmp/ok || exit 1" --health-interval 30s --health-retries 3 registry.access.redhat.com/ubi9/ubi-minimal:latest sleep 3600

Tighten the interval and retries without recreating the object:

bash
podman update --health-interval 10s --health-retries 5 upd-health

Replace the probe command:

bash
podman update --health-cmd "test -f /tmp/ready || exit 1" upd-health

Read the persisted configuration:

bash
podman inspect --format '{{json .Config.Healthcheck}}' upd-health

Sample output:

output
{"Test":["CMD-SHELL","test -f /tmp/ready || exit 1"],"Interval":10000000000,"Timeout":30000000000,"Retries":5}

Durations are nanoseconds in JSON (10000000000 = 10s). Scheduling and status fields are covered in Podman health checks.


Update restart policy

Change how Podman restarts the container after exit:

bash
podman update --restart=on-failure:5 upd-demo

Verify the stored policy:

bash
podman inspect --format '{{.HostConfig.RestartPolicy.Name}} {{.HostConfig.RestartPolicy.MaximumRetryCount}}' upd-demo

Podman 5.8.2 accepts no, never, on-failure[:max_retries], always, and unless-stopped. The new policy is stored on the container object immediately and applies to subsequent container exits — it does not restart a running container by itself.

If systemd or Quadlet owns the container, configure Restart= in [Service] rather than adding a competing Podman restart policy.


Add or change environment variables

Set a new value on the existing container:

bash
podman update --env APP_MODE=new upd-demo

Inspect shows the updated configuration:

bash
podman inspect --format '{{range .Config.Env}}{{println .}}{{end}}' upd-demo | grep APP_MODE

Sample output:

output
APP_MODE=new

A new exec session also sees the updated value:

bash
podman exec upd-demo printenv APP_MODE

Sample output:

output
new

That does not mean PID 1 picked up the change. The main process still carries the environment from its original start:

bash
podman exec upd-demo sh -c 'tr "\0" "\n" < /proc/1/environ | grep APP_MODE'

Sample output before restart:

output
APP_MODE=old

Restart the container so PID 1 starts with the new configuration:

bash
podman restart upd-demo

After restart, check PID 1 again:

bash
podman exec upd-demo sh -c 'tr "\0" "\n" < /proc/1/environ | grep APP_MODE'

Sample output:

output
APP_MODE=new

Plan for podman restart when the application reads environment only at startup — podman update --env alone is not enough for PID 1.


Remove an environment variable

Drop a variable from the container definition:

bash
podman update --unsetenv APP_MODE upd-demo

Inspect no longer lists it:

bash
podman inspect --format '{{range .Config.Env}}{{println .}}{{end}}' upd-demo | grep APP_MODE || echo "APP_MODE not in inspect"

Sample output:

output
APP_MODE not in inspect

printenv inside a running exec may also miss the variable, but PID 1 can still hold the old value until restart — verify with /proc/1/environ when the distinction matters for your workload.


Update ulimits

Raise the open-file limit in container configuration:

bash
podman update --ulimit nofile=4096:4096 upd-demo

Inspect updates immediately:

bash
podman inspect --format '{{json .HostConfig.Ulimits}}' upd-demo

Sample output (trimmed):

output
[{"Name":"RLIMIT_NOFILE","Soft":4096,"Hard":4096}]

The running main process keeps the old limit until restart:

bash
podman exec upd-demo sh -c 'grep "open files" /proc/1/limits'

Sample output before restart:

output
Max open files            1024                 1024                 files

Restart and read again:

bash
podman restart upd-demo

After restart, read PID 1 limits again:

bash
podman exec upd-demo sh -c 'grep "open files" /proc/1/limits'

Sample output after restart:

output
Max open files            4096                 4096                 files

Treat ulimit changes as effective on the next main-process start, not as a live patch to PID 1.


Rootless limitations

Resource updates still require cgroup v2 controllers your user session can write. When podman update --cpus fails with cpu controller is not available, the fix is systemd delegation into the user slice — not a different update flag. See Set container resource limits for the delegation pattern; this article only notes that podman update hits the same cgroup walls as podman run.


Which fields cannot be updated?

These changes need podman container clone, a new podman run, or a Quadlet edit — not podman update:

Field Why
Image No --image on podman update
Published ports Cannot be changed by update; clone inherits existing mappings but 5.8.2 cannot override them — recreate to change
Bind mounts and volumes Cannot be changed by update; clone can inherit them but not redefine them — recreate to change
Container command / entrypoint Immutable after create
Hostname, user namespace, many security options Create-time only

Attempting unsupported flags fails clearly:

bash
podman update --publish 8080:80 upd-demo

Sample error:

output
Error: unknown flag: --publish
See 'podman update --help'

Image swaps are blocked the same way:

bash
podman update --image alpine:latest upd-demo

Sample error:

output
Error: unknown flag: --image
See 'podman update --help'

Use podman container clone for create-time fields

Clone copies an existing container into a new name and optional new image:

bash
podman container clone upd-demo upd-clone

Syntax on Podman 5.8.2:

text
podman container clone [options] CONTAINER NAME [IMAGE]

--name is an alternative to the positional name argument. The clone inherits limits and image from the source unless you pass a third IMAGE argument.

Clone with a different image reference:

bash
podman container clone upd-demo upd-clone-img registry.access.redhat.com/ubi9/ubi-minimal:latest

That creates a new container ID — it does not mutate upd-demo. Port publishing is not available on clone in 5.8.2 (unknown flag: --publish), so expose ports with a fresh podman run or reconstruct the original command.


podman update versus clone versus recreate

Need Best starting point
CPU, memory, PID, or cpuset limits podman update
Health-check interval, command, or retries podman update
Restart policy on a plain podman run container podman update --restart=...
Environment for the next main-process start podman update then podman restart
Ulimits for the next main-process start podman update then podman restart
Different image with similar config podman container clone with IMAGE argument
Different ports, mounts, or command Recreate with podman run / Quadlet
Declarative systemd service Edit Quadlet .container and restart the unit

Quadlet users should change the unit file and let systemd recreate the container — not hand-patch a generated instance that the next systemctl restart will overwrite.


Cleanup

Remove lab containers when finished:

bash
podman rm -f upd-demo upd-health upd-clone upd-clone-img 2>/dev/null

Summary

podman update adjusts an existing container in place for cgroup limits, health probes, environment metadata, ulimits, and restart policy. CPU, memory, PID, and cpuset changes land in inspect and cgroup v2 files immediately on Podman 5.8.2. Environment and ulimit updates change container configuration right away, but PID 1 keeps its original environ and limits until podman restart — use /proc/1/environ and /proc/1/limits to verify what the main process actually runs with.

Image, ports, mounts, and most namespace settings are out of scope for update. Use podman container clone when you need a new object with a different image, or podman run when publish and mount flags change. For limit theory and rootless delegation errors, read the resource-limits guide; for health state after probe changes, read the healthcheck guide.


References

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)