# How to Use Turso's Async I/O with io_uring on Linux for Maximum Performance

> Boost Turso performance on Linux by enabling io_uring async I/O. Replace default syscalls with a high-performance ring-based interface for faster operations.

- Repository: [Turso Database/turso](https://github.com/tursodatabase/turso)
- Tags: performance
- Published: 2026-06-22

---

**Enable the `io_uring` Cargo feature at compile time and select the `"io_uring"` VFS at runtime to replace Turso's default syscall backend with a high-performance, ring-based asynchronous I/O interface on Linux.**

Turso's embedded database engine supports an optional **io_uring** backend that eliminates traditional syscall overhead through Linux's modern asynchronous I/O interface. This implementation, located in the `tursodatabase/turso` repository, provides significant latency reductions for read-heavy and write-heavy workloads by batching operations and utilizing zero-copy fixed buffers.

## Enabling the io_uring Feature at Compile Time

The io_uring backend is gated behind a Cargo feature and only compiles on Linux targets. You must explicitly opt-in during the build process.

### Configuring Cargo.toml

Add the feature flag when building the `core` crate or any workspace that depends on it:

```bash
cargo build --features=io_uring

```

According to **[`core/Cargo.toml`](https://github.com/tursodatabase/turso/blob/main/core/Cargo.toml)**, this feature pulls in the external `io-uring` crate and enables the `rustix/io_uring` implementation. The code is conditionally compiled using `#[cfg(all(target_os = "linux", feature = "io_uring", not(miri)))]`, ensuring it is excluded from non-Linux targets and Miri sanitization environments.

## Selecting the io_uring VFS at Runtime

Once compiled with the feature enabled, you must explicitly choose the io_uring backend when initializing a database connection.

### Using the Rust SDK

The SDK exposes the **`with_io`** method to specify the VFS implementation. In **[`sdk-kit/src/rsapi.rs`](https://github.com/tursodatabase/turso/blob/main/sdk-kit/src/rsapi.rs)**, this method maps the string `"io_uring"` to the corresponding backend factory:

```rust
use turso_sdk_kit::DatabaseBuilder;

fn main() -> turso_sdk_kit::Result<()> {
    let db = DatabaseBuilder::new()
        .with_path("/var/lib/turso/my.db")
        .with_io("io_uring".to_string())  // Selects the io_uring backend
        .open()?;

    let result = db.execute(
        "INSERT INTO users (name) VALUES (?)", 
        ("Alice",)
    )?;
    Ok(())
}

```

The **`with_io`** call triggers the factory logic in **[`core/lib.rs`](https://github.com/tursodatabase/turso/blob/main/core/lib.rs)**, which returns `Arc::new(UringIO::new()?)` when the string matches `"io_uring"`.

### Using the CLI

Pass the **`--vfs`** flag to the `tursodb` binary (defined in **[`cli/app.rs`](https://github.com/tursodatabase/turso/blob/main/cli/app.rs)** at line 65):

```bash
tursodb --path /var/lib/turso/my.db --vfs io_uring repl

```

This command-line argument propagates through to the same VFS factory, instantiating the `UringIO` driver for the session.

## How the io_uring Backend Works

The **`UringIO`** type defined in **[`core/io/io_uring.rs`](https://github.com/tursodatabase/turso/blob/main/core/io/io_uring.rs)** manages the complete lifecycle of the io_uring ring, from initialization through submission and completion handling.

### Ring Initialization and Lifecycle

The **`UringIO::new()`** function (lines 95-134) performs the following setup:

1. Creates an `io_uring::IoUring` instance with a submission queue capacity of **512 entries**
2. Registers a sparse fixed-buffer arena for zero-copy I/O operations
3. Probes the kernel for opcode support using `Probe` to detect available operations
4. Initializes **`RingState`**, a shared structure protected by a mutex that coordinates SQE submission
5. Allocates a `wait_lock` to serialize the `submit_and_wait` syscall

If the kernel lacks support for critical opcodes (e.g., `IORING_OP_FTRUNCATE`), the backend emits a warning and prepares synchronous fallback paths.

### Submission and Completion Path

All file operations—**`open_file`**, **`pread`**, **`pwrite`**, **`pwritev`**, **`sync`**, and **`truncate`**—construct io_uring submission queue entries (SQEs) and push them via **`RingState::submit_entry`**. Multiple threads can submit SQEs concurrently without global locking contention.

A single leader thread executes **`submit_and_wait`**, draining the completion queue (CQE) and mapping **`user_data`** keys back to Rust futures via **`completion_from_key`**. Write-vector operations (`pwritev`) that require multiple kernel calls are automatically resubmitted, with completion wakeups triggered through **`wake_user_data`**.

### Graceful Fallback Behavior

When the kernel does not support a specific opcode, Turso automatically falls back to synchronous POSIX syscalls. This ensures compatibility with older kernels while logging a warning at startup to inform operators of the degraded I/O path.

## Verification and Testing

Confirm that your deployment is utilizing the high-performance backend through logging and targeted integration tests.

### Runtime Verification

Upon successful initialization, **`UringIO::new()`** emits a debug log entry:

```

debug!("Using IO backend 'io-uring'");

```

If the backend is unavailable or the feature is disabled, the system silently falls back to the default syscall VFS.

### Integration Testing

Force the io_uring path in tests using the builder pattern shown in **[`tests/integration/query_processing/test_write_path.rs`](https://github.com/tursodatabase/turso/blob/main/tests/integration/query_processing/test_write_path.rs)** (line 1418):

```rust
#[test]
fn test_uring_write_path() {
    let db = TestBuilder::new()
        .with_io_uring(true)  // Forces io_uring backend
        .open();
    
    db.execute("PRAGMA journal_mode=WAL").unwrap();
    db.execute("INSERT INTO t (id) VALUES (1)").unwrap();
    // Uses UringIO::pwritev internally
}

```

These tests verify correct behavior under the feature flag, including proper handling of `shared_wal_*` coordination primitives.

## Summary

- **Compile-time requirement**: Enable the `io_uring` feature in [`core/Cargo.toml`](https://github.com/tursodatabase/turso/blob/main/core/Cargo.toml) and build on Linux.
- **Runtime selection**: Pass `"io_uring"` to `DatabaseBuilder::with_io()` or use `--vfs io_uring` in the CLI.
- **Architecture**: The `UringIO` type in [`core/io/io_uring.rs`](https://github.com/tursodatabase/turso/blob/main/core/io/io_uring.rs) manages a shared ring with batching (512 entries) and parallel submission capabilities.
- **Performance benefits**: Reduced syscall overhead through batched `submit_and_wait` calls and zero-copy fixed-buffer registration.
- **Compatibility**: Automatic fallback to synchronous syscalls for unsupported kernel opcodes ensures robustness across Linux versions.

## Frequently Asked Questions

### Is io_uring available on macOS or Windows?

No. The io_uring backend is strictly **Linux-only**. Attempting to use `"io_uring"` on macOS or Windows results in a runtime error: `"io_uring is only available on Linux targets"`. Windows users should use the `experimental_win_iocp` VFS instead, while macOS uses the default syscall-based VFS.

### What happens if my kernel doesn't support io_uring?

If the kernel lacks io_uring support entirely, Turso will fail to initialize the `UringIO` instance and fall back to the default VFS. If specific opcodes are missing (such as `IORING_OP_FTRUNCATE`), the backend logs a warning and executes those specific operations using standard `std::fs` syscalls while continuing to use io_uring for supported operations.

### How does io_uring improve performance over standard syscalls?

The backend reduces system-call overhead by batching up to **512 submissions** and waiting for up to **4 completions** per `io_uring_enter` syscall. Additionally, fixed-buffer registration eliminates memory copies for read/write operations, and the lock-free submission path allows multiple threads to queue operations without contention. This architecture significantly reduces context switches and CPU cycles per I/O operation.

### Can I use io_uring with the Turso hosted platform?

The io_uring backend is designed for **embedded/self-hosted deployments** where you control the underlying operating system and kernel. The Turso hosted service manages infrastructure configuration automatically and may not expose low-level VFS selection to end users. For self-hosted instances on Linux kernel 5.1+, enabling io_uring provides optimal performance for high-throughput workloads.