|

io_uring en Node.js: duplica el rendimiento en 2026

Node.js con io_uring duplicó el throughput en operaciones de E/S y bajó la latencia P99 a la mitad. Los benchmarks de junio de 2026, usando addons nativos con napi-rs y kernels Linux 5.15+, muestran entre 20 y 40% más rendimiento que el viejo epoll integrado en libuv. El cuello de botella ahora está en JavaScript, no en el kernel.

io_uring es una interfaz de E/S asincrónica del kernel Linux que reemplaza el modelo de “readiness” (epoll, select) por uno de “completion”: dos anillos en memoria compartida —Submission Queue y Completion Queue— donde las operaciones se encolan y se leen sin syscalls adicionales. Para un backend Node.js con alta concurrencia, esto significa menos cambios de contexto, buffers registrados que evitan copias, y cero llamadas al sistema cuando activás SQPOLL. La magia está en que el kernel y el usuario comparten la misma cola circular, y lo único que hacés es escribir y leer en memoria.

En 30 segundos

  • io_uring duplica el rendimiento frente a epoll en operaciones de red y filesystem con Node.js, según benchmarks de junio de 2026.
  • La integración se hace con napi-rs y Rust: bibliotecas como ferrings y node-io-uring exponen los anillos SQ/CQ a JavaScript.
  • Necesitás Linux 5.15 o superior para entornos de producción estables; kernels viejos o contenedores con seccomp restrictivo bloquean io_uring.
  • libuv solo lo usa para filesystem hoy: el soporte de red completo todavía no llegó a Node.js mainline por temas de seguridad y compatibilidad.
  • El bottleneck se mudó a JS: con io_uring, el kernel deja de ser el límite y el garbage collector pasa a ser el nuevo sospechoso.

¿Qué es io_uring y cómo se diferencia de epoll?

epoll te dice “che, hay datos listos para leer”. io_uring te dice “ya los leí, acá tenés”. La diferencia es conceptual y de performance. Ya lo cubrimos antes en revisa nuestra comparativa de pipelines CI/CD.

Con epoll, el flujo es: registrás un file descriptor, el kernel te avisa cuando está listo, hacés la syscall de lectura, el kernel copia los datos a tu buffer, y volvés a esperar. Cada paso implica un cambio de contexto usuario↔kernel. Con io_uring, vos preparás una Submission Queue Entry (SQE) con la operación que querés —lectura, escritura, accept, send— la escribís en el anillo compartido, y cuando el kernel terminó, encontrás el resultado en el Completion Queue (CQ). Si activás SQPOLL, un hilo del kernel sondea la cola sin que hagas ni una sola syscall. Cero cambios de contexto.

Según el análisis de Loke.dev, io_uring reduce los context switches drásticamente y permite zero-copy con buffers registrados. Eso en Node.js —donde libuv abstrae todo con un event loop— es un cambio de paradigma: en vez de delegar al sistema operativo operación por operación, le tirás un lote de tareas y te olvidás hasta que suenen las campanas de compleción.

¿Cuáles son las mejoras de rendimiento reportadas?

Los números no mienten, y acá hay data concreta.

El artículo de TechBytes documentó pruebas con frames de 64 KiB, redes 25 GbE y TLS offload: el throughput aumentó entre 30 y 40%, y la latencia P99 bajó casi a la mitad comparado con epoll. Lo interesante es que con zero-copy networking —usando SharedArrayBuffer para evitar que Node.js toque los datos dos veces— el sistema operativo entregaba los paquetes directamente al buffer de JavaScript sin copias intermedias. ¿El resultado? El cuello de botella se corrió del kernel al runtime de V8.

Si alguna vez tuviste un servicio Node.js saturando CPU en operaciones de red y pensaste “es culpa del sistema operativo”, la verdad es que con io_uring ese argumento se desmorona: el kernel despacha datos más rápido de lo que JavaScript los puede procesar, y ahí es donde aparecen los nuevos problemas —el garbage collector, la serialización JSON, las promesas mal manejadas—.

  • Throughput: +20-40% sostenido en cargas de red y filesystem.
  • Latencia P99: reducción de ~50% en escenarios con polling (SQPOLL activo).
  • Zero-copy: buffers registrados evitan copias kernel→userspace, clave con frames grandes.
  • CPU usage: menor overhead de syscalls, pero ojo: SQPOLL consume un core entero.

¿Cómo se integra io_uring con napi-rs?

napi-rs es un framework que te deja escribir addons nativos para Node.js en Rust, con bindings a la N-API de V8. Traducción: podés escribir código de sistemas que corre a velocidad de C, pero con la seguridad de memoria de Rust, y exponerlo a JavaScript como si fuera cualquier módulo npm. Esto se conecta con lo que analizamos en descubre cuál elegir entre Jenkins y GitHub Actions.

El proyecto ferrings hace exactamente eso: envuelve la API de io_uring en Rust, configura los buffers registrados, prepara las SQE para operaciones de red y filesystem, y expone funciones como readFile y writeFile que Node.js puede llamar directamente. El addon maneja la cola de compleción y devuelve los resultados como promesas o callbacks.

Un ejemplo conceptual —no literal, pero para que te hagas la idea— del flujo sería: preparás una SQE con el descriptor de archivo, el buffer registrado, el offset y el tamaño; la escribís en el anillo SQ; si tenés SQPOLL, el kernel la toma solo; cuando el kernel termina, leés la CQE del anillo CQ y obtenés el resultado. Todo eso en Rust, con la seguridad de que no vas a corromper memoria, y Node.js recibe un lindo integer con los bytes leídos. Ponele que antes hacías 5 syscalls por operación; ahora hacés cero (con SQPOLL) o una (sin SQPOLL, con io_uring_enter).

¿Qué requisitos de kernel y seguridad existen?

Acá viene la letra chica, y no es poca.

io_uring requiere Linux 5.1 como mínimo. Pero “mínimo” no es “recomendable”. La interfaz se estabilizó en 5.10 y alcanzó madurez en 5.15 —si estás en producción, apuntá a 5.15 o superior—. Kernels más viejos tienen bugs conocidos y funcionalidades faltantes. Si tu VPS corre un kernel 4.x porque “si funciona no lo toques”, io_uring directamente no arranca. Para entornos en Argentina donde muchos proveedores todavía ofrecen kernels long-term-support viejos, esto es un filtro importante. Si necesitás un VPS con kernel actualizado, podés pegarle una mirada a donweb.com que tiene opciones con kernels recientes.

El tema de seguridad no es menor. En 2021 y 2022, io_uring acumuló varias vulnerabilidades de escalación de privilegios —algunas explotables por usuarios no privilegiados—. Google incluso restringió io_uring en ChromeOS y Android durante un tiempo. Muchos entornos con seccomp (Docker, Kubernetes, sandboxes) bloquean la syscall io_uring_setup por defecto. Si tu plataforma de deploy usa perfiles de seguridad restrictivos, probablemente io_uring esté capado y ni te enteres.

  • Kernel mínimo: 5.1 (funciona, pero no lo uses en prod).
  • Recomendado: 5.15+ donde las APIs están estables y los bugs conocidos están parcheados.
  • seccomp y contenedores: muchos entornos bloquean io_uring por políticas de seguridad heredadas.
  • Node.js mainline: el equipo no activó io_uring para red por estas razones de seguridad y compatibilidad.

Ejemplos de implementación con ferrings y node-io-uring

Hay dos proyectos que vale la pena conocer si querés meter mano.

node-io-uring, de zbjornson, es un prototipo en C++ que implementa readFile y writeFile usando io_uring directamente. Tiene un límite de 32 requests en vuelo —es deliberado, para no saturar el anillo— y usa buffers fijos preregistrados. La API es mínima: inicializás el anillo, registrás los buffers, y llamás a las funciones de lectura/escritura que devuelven promesas. Funciona, pero el autor no lo considera listo para producción (y leyendo los issues, tiene razón).

Ferrings, por otro lado, está escrito en Rust sobre napi-rs y apunta a un scope más amplio: no solo filesystem sino también sockets, accept, y zero-copy con buffers compartidos. La arquitectura es más ambiciosa y el código es más mantenible —Rust obliga a manejar los errores de forma explícita, y el borrow checker te salva de use-after-free en los buffers registrados—. El proyecto está activo a junio de 2026 y acepta contribuciones. Te puede servir nuestra cobertura de aprende sobre SEO internacional con hreflang.

Un fragmento conceptual del flujo con ferrings: preparás la operación de lectura creando una SQE con el fd, el buffer (registrado previamente con io_uring_register_buffers), el offset y el tamaño; la insertás en el anillo SQ; si tenés SQPOLL activo, no hacés syscall —el kernel la toma del anillo por polling—; cuando el kernel completa, leés la CQE del anillo CQ y obtenés el resultado y el código de error. Todo ese baile ocurre en Rust, lejos de los ojos de JavaScript, que solo ve una promesa que se resuelve con los bytes leídos.

Comparativa: io_uring vs epoll en Node.js

Característicaepoll (libuv actual)io_uring + napi-rs
ModeloReadiness (te avisa cuándo leer)Completion (te da el resultado)
Syscalls por operación2-3 (epoll_wait + read/write)0-1 (SQPOLL elimina todas)
Zero-copyNo (copia kernel→userspace)Sí (buffers registrados)
Soporte en Node.js mainlineCompleto (red + fs)Solo filesystem en libuv
Throughput relativoBaseline+20-40%
Latencia P99Baseline-50% con SQPOLL
Complejidad de implementaciónBaja (abstraída por libuv)Alta (requiere Rust/C++ y manejo de anillos)
Riesgos de seguridadMaduro, pocas vulnerabilidadesHistorial de CVEs, requiere kernel actualizado
rendimiento io_uring Node.js diagrama explicativo

Qué está confirmado y qué no

✅ Confirmado

  • io_uring en libuv para operaciones de filesystem ya está mergeado en Node.js (desde la versión 21.x, estabilizado en 2026).
  • Los benchmarks independientes de TechBytes y Loke.dev muestran +20-40% de throughput en cargas de red con addons nativos.
  • Ferrings y node-io-uring son funcionales como prototipos y demuestran la viabilidad de la integración con napi-rs.
  • Linux 5.15+ es el piso firme para producción; kernels anteriores tienen bugs documentados.

❌ No confirmado o pendiente

  • El soporte completo de red en libuv con io_uring no tiene fecha. El equipo de Node.js priorizó la estabilidad y seguridad por sobre la performance.
  • No hay benchmarks de producción a gran escala (miles de conexiones concurrentes reales, no sintéticas).
  • La compatibilidad con plataformas serverless (AWS Lambda, Cloudflare Workers) es nula porque el kernel está fuera de tu control.
  • El overhead de SQPOLL (un core al 100% constantemente) no está bien medido en cargas mixtas CPU/IO.

Errores comunes al implementar io_uring en Node.js

He visto varios equipos estrellarse contra la misma pared. Acá van los tres más frecuentes.

Para más detalle en optimizaciones Linux, revisá nuestro artículo sobre io_uring Node.js y su revolución en I/O.

  • Asumir que funciona en cualquier contenedor. Docker con seccomp default bloquea io_uring_setup. Si tu addon hace la syscall y se encuentra con un EPERM, no es un bug de tu código —es la política de seguridad del runtime—. Probá con --security-opt seccomp=unconfined solo en desarrollo, no en producción.
  • No registrar los buffers y esperar zero-copy. Si no llamás a io_uring_register_buffers, el kernel hace copia. Punto. La magia del zero-copy requiere que los buffers estén fijados en memoria —el kernel necesita saber que no los vas a mover mientras opera—. Muchos se saltean este paso y después no entienden por qué el rendimiento es igual al de epoll.
  • SQPOLL sin control de CPU. SQPOLL es un hilo del kernel que sondea el anillo SQ constantemente. Eso significa un core al 100% todo el tiempo, incluso cuando no hay tráfico. Si lo activás en una máquina de 2 cores, felicitaciones, le sacaste el 50% de CPU a tu aplicación. Solo vale la pena en servidores dedicados con núcleos de sobra y cargas sostenidas de E/S.

Preguntas Frecuentes

¿Qué es io_uring y para qué sirve en Node.js?

io_uring es una interfaz de E/S asincrónica del kernel Linux introducida en la versión 5.1. Para Node.js, permite ejecutar operaciones de lectura, escritura y red con menos syscalls y zero-copy, lo que se traduce en más throughput y menor latencia comparado con el epoll que usa libuv por defecto. Lo explicamos a fondo en ejecuta agentes locales sin API con OpenClaw.

¿Cuánto más rápido es io_uring que epoll en Node.js?

Los benchmarks de 2026 reportan entre 20 y 40% más throughput y una reducción de ~50% en latencia P99 para cargas de red con frames de 64 KiB en redes 25 GbE. Con SQPOLL activo, las operaciones pueden ejecutarse sin ninguna syscall, eliminando por completo los cambios de contexto.

¿Puedo usar io_uring en producción con Node.js hoy?

Para operaciones de filesystem, sí —libuv lo incorporó desde Node.js 21.x y está estable en 2026—. Para red, solo mediante addons nativos con napi-rs como ferrings, y con las salvedades de seguridad y compatibilidad de kernel (necesitás Linux 5.15+ y seccomp permisivo).

¿Qué kernel necesito para usar io_uring?

Linux 5.1 como mínimo funcional, 5.10 para estabilidad básica, y 5.15 o superior para entornos de producción. Kernels más viejos no exponen la syscall o tienen bugs que pueden corromper datos. En contenedores, verificá que el perfil de seccomp no bloquee io_uring_setup.

¿Cómo se integra io_uring con napi-rs?

napi-rs permite escribir addons nativos en Rust que se compilan como módulos de Node.js. Bibliotecas como ferrings envuelven la API de io_uring (creación de anillos, registro de buffers, encolado de SQE y lectura de CQE) y la exponen a JavaScript como funciones asincrónicas con promesas.

Conclusión

io_uring en Node.js duplicó el rendimiento en benchmarks controlados y demostró que el modelo de completación le gana por goleada al de readiness cuando hay E/S densa. Pero la adopción no es un switch que encendés y listo.

Hoy, a junio de 2026, podés usarlo para filesystem en Node.js mainline sin dramas. Para red, necesitás addons con napi-rs, un kernel 5.15+, y un entorno de ejecución que no bloquee la syscall. No es para todos los proyectos, y probablemente no sea para el CRUD con 100 requests por minuto. Pero si estás construyendo un proxy, un API gateway, o cualquier servicio donde la E/S sea el cuello —y tenés control del kernel—, es el momento de probarlo en staging.

La pelota está del lado del equipo de Node.js para integrar io_uring en libuv para red de forma nativa. Mientras tanto, ferrings y node-io-uring demuestran que el camino existe. El que quiera velocidad hoy, va a tener que ensuciarse las manos con Rust.

Fuentes

Te puede interesar...