epoll vs io_uring for SREs

A recent Hacker News discussion around epoll vs io_uring in Linux is a useful reminder for operators: the async I/O model under a proxy, database, queue, or agent runtime can become an incident variable.
Linux epoll has been the default answer for scalable readiness notification for years. Linux io_uring is the newer completion-based interface built around shared submission and completion rings between user space and the kernel.
What Is the Difference?
epoll tells a process that a file descriptor is ready for I/O. The application still calls read(), write(), accept(), or another syscall after it receives the event. That model is familiar, portable across many event loops, and well understood by production teams.
io_uring flips the shape of the loop. The application submits operations into a submission queue, and the kernel posts completion queue events when work is done. That can reduce syscall overhead, batch more work, and support advanced paths such as registered buffers, multishot operations, and submission queue polling.
Why SRE Teams Should Care
- CPU overhead:
epollcan spend more time crossing the user-kernel boundary under heavy I/O. - Latency shape:
io_uringcan improve tail behavior for some workloads, but only after careful batching and queue sizing. - Failure mode:
epollreadiness bugs often look like stalled event loops, whileio_uringfailures arrive asynchronously in completion results. - Kernel dependency:
io_uringrequires a newer Linux kernel and has changed quickly compared withepoll. - Operational visibility: both models need metrics for queue depth, active connections, retries, and error codes.
Quick Evaluation Checklist
Before moving a service from epoll to io_uring, test it like an infrastructure change, not a micro-optimization.
uname -r
grep -R "io_uring" /boot/config-$(uname -r) 2>/dev/null || true
perf stat -p <pid> -- sleep 30
ss -tan state established | wc -l
Compare syscall rate, CPU utilization, p99 latency, connection churn, and memory pressure during realistic load. Include slow clients, short connections, TLS, disk I/O, and backpressure. A benchmark that only measures happy-path throughput can hide the exact behaviors that matter during an outage.
Operational Tips
Keep epoll for mature services where portability, predictable debugging, and known edge-triggered behavior matter more than peak throughput. It is still a strong default for many network daemons.
Evaluate io_uring when a service is Linux-only, I/O-heavy, and bottlenecked on syscall overhead or context switching. Pin kernel versions in staging, watch release notes, and make sure your tracing and profiling tools can explain queue behavior before production rollout.
Be careful with submission queue polling. IORING_SETUP_SQPOLL can reduce syscall overhead, but it uses a kernel polling thread and can burn CPU while idle. That tradeoff may be fine for a saturated proxy and bad for a bursty internal service.
Conclusion
io_uring is an important direction for modern Linux I/O, but epoll is not obsolete just because a newer API exists. SRE teams should treat the choice as an operational design decision: measure the workload, understand the failure modes, and ship the simpler model unless the data supports the move.
Want to operate AI agents with the same discipline you apply to production services? Akmatori helps SRE teams run autonomous agents with clear workflows, controlled execution, and infrastructure-aware automation. Akmatori is built by Gcore, the global edge AI, cloud, network, and security provider.
