#29790·podman

compat API: POST /containers/{id}/update resets RestartPolicy to `no` when the body omits it

Author: dirkwaCreated Sep 17, 2026Updated Sep 17, 2026

Issue Description

The Docker-compat POST /containers/{id}/update endpoint resets a container's restart policy to no whenever the request body does not carry RestartPolicy. Docker Engine's endpoint keeps the stored policy when the field is omitted and only changes it when RestartPolicy.Name is set. A resources-only update body, which is what docker update --memory ...-style clients send, therefore silently turns unless-stopped / always into no: the container no longer restarts after a crash and does not come back at boot.

Cause: UpdateContainer in pkg/api/handlers/compat/containers.go always hands ctr.Update a restart-policy pointer, even when the body had none:

go
	// Restart policy
	localPolicy := string(options.RestartPolicy.Name)
	restartPolicy := &localPolicy

With the field omitted, options.RestartPolicy.Name is "", define.ValidateRestartPolicy("") accepts it (empty means none), and libpod's update stores it, so inspect then reports no. The same lines are present in v5.4.2, v5.6.0, v6.1.0 and current main (main around lines 878–880).

Suggested fix: pass restartPolicy (and restartRetries) only when options.RestartPolicy.Name != "", so an omitted field leaves the stored policy alone as Docker does.

Steps to reproduce the issue

bash
$ SOCK=$XDG_RUNTIME_DIR/podman/podman.sock
$ curl -s --unix-socket $SOCK -X POST "http://d/v1.41/containers/create?name=t" \
    -H 'Content-Type: application/json' \
    -d '{"Image":"docker.io/library/alpine:3.19","Cmd":["sleep","3600"],"HostConfig":{"RestartPolicy":{"Name":"unless-stopped"}}}'
$ curl -s --unix-socket $SOCK -X POST "http://d/v1.41/containers/t/start"
$ podman inspect t --format '{{.HostConfig.RestartPolicy.Name}}'
unless-stopped
$ curl -s --unix-socket $SOCK -X POST "http://d/v1.41/containers/t/update" \
    -H 'Content-Type: application/json' -d '{"Memory":67108864}'
{"Warnings":null}
$ podman inspect t --format '{{.HostConfig.RestartPolicy.Name}}'
no

Sending {"Memory":67108864,"RestartPolicy":{"Name":"unless-stopped"}} instead keeps (or sets) the policy, so clients can work around it by always naming the policy in every update body.

Describe the results you received

After an update body carrying only Memory, HostConfig.RestartPolicy.Name changes from unless-stopped to no. The memory limit itself is applied.

Describe the results you expected

The restart policy is unchanged when the update body omits RestartPolicy. That is what Docker Engine does: the same two requests against Docker Engine 29.8.0 leave the policy at unless-stopped, and an explicit RestartPolicy in the body is applied on both engines.

podman info output

podman info
yaml
host:
  arch: amd64
  buildahVersion: 1.39.3
  cgroupControllers:
  - cpuset
  - cpu
  - io
  - memory
  - pids
  cgroupManager: systemd
  cgroupVersion: v2
  conmon:
    package: conmon_2.1.12-4_amd64
    path: /usr/bin/conmon
    version: 'conmon version 2.1.12, commit: unknown'
  cpuUtilization:
    idlePercent: 91.05
    systemPercent: 1.61
    userPercent: 7.34
  cpus: 4
  databaseBackend: sqlite
  distribution:
    codename: trixie
    distribution: debian
    version: "13"
  eventLogger: journald
  freeLocks: 2028
  hostname: vmsignalk
  idMappings:
    gidmap:
    - container_id: 0
      host_id: 1000
      size: 1
    - container_id: 1
      host_id: 100000
      size: 65536
    uidmap:
    - container_id: 0
      host_id: 1000
      size: 1
    - container_id: 1
      host_id: 100000
      size: 65536
  kernel: 6.12.107+deb13-amd64
  linkmode: dynamic
  logDriver: journald
  memFree: 1002446848
  memTotal: 16800845824
  networkBackend: netavark
  networkBackendInfo:
    backend: netavark
    dns:
      package: aardvark-dns_1.14.0-3_amd64
      path: /usr/lib/podman/aardvark-dns
      version: aardvark-dns 1.14.0
    package: netavark_1.14.0-2_amd64
    path: /usr/lib/podman/netavark
    version: netavark 1.14.0
  ociRuntime:
    name: crun
    package: crun_1.21-1_amd64
    path: /usr/bin/crun
    version: |-
      crun version 1.21
      commit: 10269840aa07fb7e6b7e1acff6198692d8ff5c88
      rundir: /run/user/1000/crun
      spec: 1.0.0
      +SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +CRIU +WASM:wasmedge +YAJL
  os: linux
  pasta:
    executable: /usr/bin/pasta
    package: passt_0.0~git20250503.587980c-2+deb13u1_amd64
    version: ""
  remoteSocket:
    exists: true
    path: /run/user/1000/podman/podman.sock
  rootlessNetworkCmd: pasta
  security:
    apparmorEnabled: false
    capabilities: CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_FOWNER,CAP_FSETID,CAP_KILL,CAP_NET_BIND_SERVICE,CAP_SETFCAP,CAP_SETGID,CAP_SETPCAP,CAP_SETUID,CAP_SYS_CHROOT
    rootless: true
    seccompEnabled: true
    seccompProfilePath: /usr/share/containers/seccomp.json
    selinuxEnabled: false
  serviceIsRemote: false
  slirp4netns:
    executable: /usr/bin/slirp4netns
    package: slirp4netns_1.2.1-1.1_amd64
    version: |-
      slirp4netns version 1.2.1
      commit: 09e31e92fa3d2a1d3ca261adaeb012c8d75a8194
      libslirp: 4.8.0
      SLIRP_CONFIG_VERSION_MAX: 5
      libseccomp: 2.6.0
  swapFree: 6722920448
  swapTotal: 8588881920
  uptime: 71h 32m 34.00s (Approximately 2.96 days)
  variant: ""
plugins:
  authorization: null
  log:
  - k8s-file
  - none
  - passthrough
  - journald
  network:
  - bridge
  - macvlan
  - ipvlan
  volume:
  - local
registries: {}
store:
  configFile: /home/dirk/.config/containers/storage.conf
  containerStore:
    number: 13
    paused: 0
    running: 12
    stopped: 1
  graphDriverName: overlay
  graphOptions: {}
  graphRoot: /home/dirk/.local/share/containers/storage
  graphRootAllocated: 307423244288
  graphRootUsed: 212082479104
  graphStatus:
    Backing Filesystem: extfs
    Native Overlay Diff: "true"
    Supports d_type: "true"
    Supports shifting: "false"
    Supports volatile: "true"
    Using metacopy: "false"
  imageCopyTmpDir: /var/tmp
  imageStore:
    number: 29
  runRoot: /run/user/1000/containers
  transientStore: false
  volumePath: /home/dirk/.local/share/containers/storage/volumes
version:
  APIVersion: 5.4.2
  BuildOrigin: Debian
  Built: 1766335321
  BuiltTime: Mon Dec 22 04:42:01 2025
  GitCommit: ""
  GoVersion: go1.24.4
  Os: linux
  OsArch: linux/amd64
  Version: 5.4.2

Podman in a container

No

Privileged Or Rootless

Rootless

Upstream Latest Release

No — measured on 5.4.2 (Debian 13 package). The handler code is identical in v6.1.0 and on main, so I expect the same result there, but the reproducer above was only run on 5.4.2.

Additional environment details

Debian 13 (trixie), x86_64, rootless, cgroup v2, crun. In normal operation the endpoint is reached through dockerode (a Node Docker API client); the curl sequence above is the minimal form.

Additional information

Found via signalk-container, a Signal K plugin that manages containers through the compat API. A periodic resource update that omitted the field left every managed container with RestartPolicy: no (reported and root-caused by humppafreak in dirkwa/signalk-container#289; worked around in dirkwa/signalk-container#290 by always sending the policy).

Source: podman-container-tools/podman