Issue Description
When fetching events with the --until flag on a system using the file events backend, libpod/events/logfile.go spawns an unmanaged, blocking goroutine that ignores context.Context cancellation.
if len(options.Until) > 0 {
// ...
go func() {
time.Sleep(time.Until(untilTime))
if err := t.Stop(); err != nil {
logrus.Errorf("Stopping logger: %v", err)
}
}()
}
Because time.Sleep is not context-aware, if a client cancels the request (e.g., Ctrl+C on the CLI, or a dropped HTTP connection on the REST API), the goroutine is orphaned and continues sleeping in the background.
If running Podman as a service (podman system service), a client that repeatedly polls GET /events?until= and drops the connection will cause a silent goroutine leak, stacking up sleeping routines and wasting resources.
Steps to reproduce the issue
Steps to reproduce the issue
1. Start the Podman API service (`podman system service`).
2. Send a request to the events endpoint with a far-future until parameter: `curl -v --unix-socket /run/user/1000/podman/podman.sock "http://d/v4.0.0/libpod/events?until=9999h"`
3. Immediately cancel the curl request (press `Ctrl+C`).
4. The background goroutine in `logfile.go` remains asleep, waiting out the 9999h timer even though the context was cancelled.
### Describe the results you received
The goroutine uses an un-interruptible `time.Sleep` which causes a memory/goroutine leak when requests are terminated early by the client.
### Describe the results you expected
The goroutine should use a `timer` and `select` on `ctx.Done()` so it exits immediately when the parent request is cancelled, properly freeing system resources.
### podman info output
```yaml
<details>
<summary>podman info</summary>
time="2026-08-12T16:08:40Z" level=warning msg="\"/\" is not a shared mount, this could cause issues or missing mounts with rootless containers"
host:
arch: amd64
buildahVersion: 1.33.7
cgroupControllers:
- cpu
- memory
- pids
cgroupManager: cgroupfs
cgroupVersion: v2
conmon:
package: conmon_2.1.10+ds1-1build2_amd64
path: /usr/bin/conmon
version: 'conmon version 2.1.10, commit: unknown'
cpuUtilization:
idlePercent: 98.71
systemPercent: 0.3
userPercent: 0.99
cpus: 8
databaseBackend: sqlite
distribution:
distribution: ubuntu
version: "22.04"
eventLogger: file
hostname: MSI
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: 5.15.153.1-microsoft-standard-WSL2
linkmode: dynamic
logDriver: journald
memFree: 2269925376
memTotal: 8251031552
networkBackend: netavark
networkBackendInfo:
backend: netavark
dns:
package: '"aardvark-dns" is not installed.'
path: /usr/lib/podman/aardvark-dns
version: aardvark-dns 1.10.0
package: '"netavark" is not installed.'
path: /usr/lib/podman/netavark
version: netavark 1.10.3
ociRuntime:
name: crun
package: crun_1.14.4-1ubuntu1_amd64
path: /usr/bin/crun
version: |-
crun version 1.14.4
commit: 260fa110753060f6ee4a8e8e7a027376c72d512a
rundir: /run/user/1000/crun
spec: 1.0.0
+SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +CRIU +YAJL
os: linux
pasture: true
remoteSocket:
exists: true
path: /run/user/1000/podman/podman.sock
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: /etc/containers/seccomp.json
selinuxEnabled: false
slirp4netns:
executable: /usr/bin/slirp4netns
package: slirp4netns_1.0.1-2_amd64
version: |-
slirp4netns version 1.0.1
commit: 2011b9a2dc4b4ad44ff9cc3f25c7657929497e74
libslirp: 4.6.1
SLIRP_CONFIG_VERSION_MAX: 3
libseccomp: 2.5.3
swapFree: 2147483648
swapTotal: 2147483648
uptime: 462h 21m 27.67s (Approximately 19.25 days)
plugins:
authorization: null
log:
- k8s-file
- none
- passthrough
- journald
network:
- bridge
- macvlan
- ipvlan
volume:
- local
registries:
search:
- registry.fedoraproject.org
- registry.access.redhat.com
- docker.io
- quay.io
store:
configFile: /home/dhruv/.config/containers/storage.conf
containerStore:
number: 1
paused: 0
running: 1
stopped: 0
graphDriverName: overlay
graphOptions: {}
graphRoot: /home/dhruv/.local/share/containers/storage
graphRootAllocated: 274864197632
graphRootUsed: 19572428800
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: 0
runRoot: /run/user/1000/containers
transientStore: false
volumePath: /tmp/podman_temp_root/volumes
version:
APIVersion: 4.9.3
Built: 0
BuiltTime: Thu Jan 1 00:00:00 1970
GitCommit: ""
GoVersion: go1.22.2
Os: linux
OsArch: linux/amd64
Version: 4.9.3
</details>
Podman in a container
No
Privileged Or Rootless
Rootless
Upstream Latest Release
Yes
Additional environment details
No response
Additional information
I plan to submit a PR to fix this issue as my third contribution for my LFX Mentorship application. The PR will replace time.Sleep with a time.NewTimer and a select block listening on ctx.Done() to guarantee the goroutine is successfully destroyed.
Issue Description
When fetching events with the
--untilflag on a system using thefileevents backend,libpod/events/logfile.gospawns an unmanaged, blocking goroutine that ignorescontext.Contextcancellation.Because
time.Sleepis not context-aware, if a client cancels the request (e.g.,Ctrl+Con the CLI, or a dropped HTTP connection on the REST API), the goroutine is orphaned and continues sleeping in the background.If running Podman as a service (podman system service), a client that repeatedly polls GET /events?until= and drops the connection will cause a silent goroutine leak, stacking up sleeping routines and wasting resources.
Steps to reproduce the issue
Steps to reproduce the issue
Podman in a container
No
Privileged Or Rootless
Rootless
Upstream Latest Release
Yes
Additional environment details
No response
Additional information
I plan to submit a PR to fix this issue as my third contribution for my LFX Mentorship application. The PR will replace
time.Sleepwith atime.NewTimerand aselectblock listening onctx.Done()to guarantee the goroutine is successfully destroyed.