Native Transport Reference
Quarkus supports Netty’s native transports to improve I/O performance on specific platforms. This guide explains how to configure and use native transports in your Quarkus application.
1. What are native transports?
By default, Quarkus uses Java NIO for network I/O, which works across all platforms but relies on the JVM’s abstraction layer. Netty provides native transports that bypass this abstraction and directly use platform-specific system calls:
-
epoll — Linux-specific transport using the epoll API
-
kqueue — macOS/BSD transport using the kqueue API
-
io_uring — Linux-specific transport using the io_uring API
Native transports can provide:
-
Lower latency for I/O operations
-
Reduced CPU overhead
-
Better scalability under high connection counts
-
Access to platform-specific socket options
The performance benefit varies by workload. Applications with high I/O volume or many concurrent connections typically see the most improvement.
2. Supported transports
| Transport | Platform | Dependency | Classifiers | Native executable |
|---|---|---|---|---|
epoll |
Linux |
|
|
Supported |
kqueue |
macOS, BSD |
|
|
Not yet supported |
io_uring |
Linux 5.1+ |
|
|
Supported |
3. Quick start
To use native transports, add the appropriate dependencies and enable native transport in your configuration.
3.1. Add dependencies
Add the native transport dependencies matching your deployment platform. You can include multiple transports to support different platforms from the same build.
<!-- Linux epoll (x86) -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<classifier>linux-x86_64</classifier>
</dependency>
<!-- Linux epoll (ARM) -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<classifier>linux-aarch_64</classifier>
</dependency>
<!-- Linux io_uring (x86) -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-io_uring</artifactId>
<classifier>linux-x86_64</classifier>
</dependency>
<!-- Linux io_uring (ARM) -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-io_uring</artifactId>
<classifier>linux-aarch_64</classifier>
</dependency>
<!-- OSX KQueue (x86) -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-kqueue</artifactId>
<classifier>osx-x86_64</classifier>
</dependency>
<!-- OSX KQueue (Apple Silicon) -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-kqueue</artifactId>
<classifier>osx-aarch_64</classifier>
</dependency>
// Linux epoll
implementation("io.netty:netty-transport-native-epoll::linux-x86_64")
implementation("io.netty:netty-transport-native-epoll::linux-aarch_64")
// Linux io_uring
implementation("io.netty:netty-transport-native-io_uring::linux-x86_64")
implementation("io.netty:netty-transport-native-io_uring::linux-aarch_64")
// macOS kqueue
implementation("io.netty:netty-transport-native-kqueue::osx-x86_64")
implementation("io.netty:netty-transport-native-kqueue::osx-aarch_64")
| The version is managed by the Quarkus BOM, so you do not need to specify it explicitly. |
3.2. Enable native transport
Add the following to your application.properties:
quarkus.vertx.native-transport=if-available
Or in application.yml:
quarkus:
vertx:
native-transport: if-available
3.3. Verify native transport is active
When native transport is successfully loaded, you will see a log message at startup:
[io.qua.ver.cor.run.VertxCoreRecorder] (main) Vertx has Native Transport Enabled: true
If native transport dependencies are missing, you will see:
[io.qua.ver.cor.run.VertxCoreProcessor] (build) No native transport detected. Consider adding io.netty:netty-transport-native-epoll or io.netty:netty-transport-native-kqueue dependency.
4. Configuration reference
Quarkus provides two configuration properties to control native transport behavior.
Both are build-time properties (fixed at build time), meaning they are resolved during the Quarkus augmentation phase.
They can be set in application.properties and overridden using Quarkus profiles (e.g., %dev, %prod), but cannot be changed at runtime via environment variables.
| Property | Default | Description |
|---|---|---|
|
|
Controls whether and how strictly native transport should be used. Accepted values: |
|
|
Select a specific transport: |
4.1. Property relationships
-
Setting
native-transport=disableddisables native transport entirely. -
Setting
native-transport-typeto a specific value (e.g.,epoll) implicitly enables native transport, even ifnative-transportisdisabled. -
When
native-transport=required, missing dependencies cause a build failure, and a transport that cannot load at runtime causes a startup failure.
4.2. Example configurations
quarkus.vertx.native-transport=if-available
quarkus.vertx.native-transport=required
quarkus.vertx.native-transport-type=epoll
quarkus.vertx.native-transport=if-available
quarkus.vertx.native-transport-type=io-uring
4.3. Profile-based configuration
Since native transport properties are fixed at build time, you can use Quarkus profiles to configure different transports per environment. This is useful when developing on macOS and deploying on Linux:
# Dev mode: no native transport needed (works on macOS or Linux)
%dev.quarkus.vertx.native-transport=disabled
# Production: require io_uring on Linux
%prod.quarkus.vertx.native-transport=required
%prod.quarkus.vertx.native-transport-type=io-uring
The %dev profile applies during quarkus dev, while %prod applies when building production artifacts.
This lets you develop locally without native transport dependencies and enforce them in production.
5. Build-time and runtime checks
Quarkus performs two-stage validation of native transport configuration:
5.1. Build-time validation
During the build, Quarkus scans the classpath for native transport dependencies and validates them against the configuration:
-
If
native-transport-typeis set to a specific transport (e.g.,epoll) but the corresponding dependency is missing:-
When
native-transport=required: the build fails with aConfigurationException. -
When
native-transport=if-available: the build logs a warning and continues. The application will fall back to Java NIO at runtime.
-
-
If
native-transport=if-availablebut no native transport dependency is found, the build logs a warning.
5.2. Runtime behavior
Even when a transport dependency is on the classpath, the native library may fail to load at runtime (e.g., running on a different platform, or the kernel does not support io_uring). Quarkus checks the actual transport status at startup:
5.2.1. When native-transport=if-available
If the transport fails to load, Quarkus logs a warning and falls back to Java NIO.
6. Platform-specific socket options
Native transports provide access to platform-specific socket options not available with Java NIO.
6.1. Linux (epoll and io_uring)
6.1.1. SO_REUSEPORT
Allows multiple sockets to bind to the same address and port. The kernel distributes incoming connections across the sockets, improving scalability.
quarkus.http.so-reuse-port=true
6.1.2. TCP_QUICKACK
Disables delayed ACKs, reducing latency for request-response protocols.
quarkus.http.tcp-quick-ack=true
6.1.3. TCP_CORK
Buffers small packets until the buffer is full or the cork is removed, reducing the number of packets sent.
quarkus.http.tcp-cork=true
7. epoll vs io_uring
Both epoll and io_uring are available on Linux. Which should you use?
7.1. Use epoll if
-
You need a stable, battle-tested transport
-
Your application primarily handles network I/O
-
You are running on an older kernel (pre-5.1)
-
You need the widest compatibility
7.2. Use io_uring if
-
You have a mixed workload with file and network I/O
-
You are running a modern kernel (5.6+)
-
You want to experiment with cutting-edge I/O performance
-
You can tolerate experimental features
| This article, from Nicolas J. Altmann, gives a gentle introduction to the differences between epoll and io_uring. |
8. io_uring details
io_uring is a modern Linux I/O interface that provides unified semantics for file and network I/O.
8.1. Kernel requirements
-
Minimum: Linux kernel 5.1
-
Recommended: Linux kernel 5.6 or later
Earlier kernels either do not support io_uring or have significant bugs and performance issues.
9. Native executable support
Native transports can be used in Quarkus native executables on Linux. epoll and io_uring are supported in native mode. kqueue is not yet supported in native executables.
When building a native executable with native transport dependencies on the classpath, the transport is available at runtime just as in JVM mode. No additional configuration is needed beyond the standard setup described in this guide.
10. Troubleshooting
10.1. Wrong classifier
If you see a warning about native transport being unavailable, verify that your dependency uses the correct classifier for your platform.
Common classifiers:
-
Linux x86_64:
linux-x86_64 -
Linux aarch64:
linux-aarch_64 -
macOS x86_64:
osx-x86_64 -
macOS aarch64:
osx-aarch_64
Example error:
WARN [io.qua.ver.cor.run.VertxCoreRecorder] (main) Native transport was requested but is not available.
Solution: Add the dependency with the correct classifier:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<classifier>linux-x86_64</classifier>
</dependency>