io_uring async I/O feature gate¶
The IOUring feature gate enables PostgreSQL 18's asynchronous I/O backend (io_method=io_uring) for a DocumentDB cluster. Because io_uring requires relaxing the container's seccomp sandbox, the feature is opt-in and disabled by default.
Overview¶
PostgreSQL 18 introduces a pluggable asynchronous I/O subsystem. On Linux, the io_uring backend submits read I/O through the kernel's io_uring interface, which overlaps storage latency instead of blocking on each read.
DocumentDB doesn't turn this on for you automatically, for one reason: security. io_uring has been a recurring kernel-exploit surface, so the container runtime's RuntimeDefault seccomp profile strips the io_uring_setup, io_uring_enter, and io_uring_register syscalls. CloudNative-PG (CNPG) runs the PostgreSQL pods with seccompProfile=RuntimeDefault, so without intervention PostgreSQL crashes at startup with:
Enabling io_uring therefore means relaxing seccomp — a security trade-off that the Kubernetes cluster operator must consciously accept. DocumentDB makes that choice explicit through the IOUring feature gate rather than enabling it silently.
Note
The IOUring gate controls one DocumentDB cluster. The seccomp profile (which Localhost profile the operator points the pods at) is configured once on the operator and applies to every DocumentDB cluster it manages. See Seccomp configuration.
What enabling the gate does¶
When you set spec.featureGates.IOUring: true, the operator does two things natively — no external Kyverno policy or admission webhook is required:
- Sets
io_method=io_uringas a protected PostgreSQL parameter. This value can't be overridden throughspec.postgres.parameters. (See PostgreSQL parameter tuning for how protected parameters work.) - Relaxes the PostgreSQL container's seccomp profile so the three io_uring syscalls are allowed. The operator points the CNPG cluster's pod security context at a Localhost seccomp profile (see Seccomp configuration).
When the gate is disabled (the default), the operator changes nothing and CNPG keeps its hardened RuntimeDefault profile.
How to enable¶
Add the feature gate to your DocumentDB custom resource:
apiVersion: documentdb.io/preview
kind: DocumentDB
metadata:
name: my-documentdb
spec:
nodeCount: 1
instancesPerNode: 1
resource:
storage:
pvcSize: "50Gi"
featureGates:
IOUring: true # (1)!
- Opt in to PostgreSQL 18
io_method=io_uring. The operator also relaxes the PostgreSQL container seccomp profile using the operator-level Localhost profile.
For the full field reference, see DocumentDBSpec in the API Reference.
Seccomp configuration¶
Which Localhost seccomp profile the operator points the pods at is operator-level configuration, set through an environment variable on the operator deployment. The same profile applies to all DocumentDB clusters managed by that operator.
| Environment variable | Values | Default | Description |
|---|---|---|---|
DOCUMENTDB_IOURING_SECCOMP_PROFILE |
profile path | profiles/documentdb-iouring.json |
Localhost profile path, relative to the node's kubelet seccomp root (/var/lib/kubelet/seccomp). |
With the bundled Helm chart, set this through a first-class value (preferred):
helm upgrade --install documentdb-operator <chart> -n documentdb-operator \
--set operator.ioUring.seccompProfile=profiles/documentdb-iouring.json
Leaving the value empty keeps the operator's built-in default
(profiles/documentdb-iouring.json). For an already-installed operator you can patch
the manager container env directly instead:
spec:
template:
spec:
containers:
- name: documentdb-operator
env:
- name: DOCUMENTDB_IOURING_SECCOMP_PROFILE
value: "profiles/documentdb-iouring.json"
The operator points the PostgreSQL pods at a Localhost seccomp profile that re-allows only the three io_uring syscalls on top of the runtime default. This keeps the rest of the sandbox intact.
The referenced profile JSON — the upstream RuntimeDefault profile plus io_uring_setup, io_uring_enter, and io_uring_register — must be pre-installed on every node that runs PostgreSQL pods, at the path resolved under /var/lib/kubelet/seccomp. If the profile is missing on a node, the pod scheduled there fails to start.
The hands-on io_uring feature playground provides the curated profile plus a kind extraMount and a DaemonSet installer that distribute it to every node.
Security trade-off
Relaxing seccomp — even with the hardened Localhost profile that re-allows only the three io_uring syscalls — widens the kernel attack surface. io_uring has been a recurring kernel-exploit vector, so this is a trade-off you accept as the cluster operator. That is why the gate is opt-in and disabled by default.
Prerequisites¶
- PostgreSQL 18 image.
io_method=io_uringexists only in PostgreSQL 18 and later. Make sure the cluster runs a PG18 image. - A node kernel with io_uring enabled. io_uring must not be disabled at the kernel level — the
kernel.io_uring_disabledsysctl must be0(the kernel default;0means not disabled, i.e. enabled), mirroring theIOUring: truegate you set on the cluster. Modern AKS, EKS, and GKE node images qualify. - The seccomp profile installed on nodes. The Localhost profile referenced by
DOCUMENTDB_IOURING_SECCOMP_PROFILEmust exist on every node that runs PostgreSQL pods. The io_uring feature playground automates this.
Verification¶
After enabling the gate and waiting for the rolling restart to finish, confirm io_uring is active.
-
Check that the PostgreSQL pods are running (not CrashLooping):
-
Confirm
io_methodisio_uringby connecting to PostgreSQL:Expected output:
-
Inspect the pod's seccomp profile to confirm the operator relaxed it:
This shows
{"type":"Localhost","localhostProfile":"profiles/documentdb-iouring.json"}. -
Confirm reads are flowing through the I/O path with
pg_stat_io:
Performance¶
io_uring's measured benefit is primarily tail-latency stability on I/O-bound scans, not raw throughput. On Azure Premium SSD at low concurrency, io_method=io_uring delivers lower, more predictable p95/p99 latency on heavy range scans and reduces in-engine read-wait time, while point lookups and aggregate throughput are largely unchanged.
Troubleshooting¶
PostgreSQL CrashLoops with "could not setup io_uring queue"¶
If a PostgreSQL pod restarts repeatedly and its logs show:
the seccomp profile wasn't relaxed for that pod. Check, in order:
- Profile not installed on the node. The profile JSON is missing on the node where the pod is scheduled. Install it on every node that runs PostgreSQL pods — the io_uring feature playground DaemonSet handles this.
- Wrong profile path.
DOCUMENTDB_IOURING_SECCOMP_PROFILEdoesn't match the actual file path under/var/lib/kubelet/seccompon the node. Align the env var with the installed file. - Operator not restarted. The profile env var changed but the operator deployment wasn't rolled, so new clusters still reference the old path. Verify the pod's seccomp profile with the verification command above.
Related¶
- PostgreSQL parameter tuning — how protected parameters such as
io_methodare managed - API Reference: DocumentDBSpec — the
featureGatesfield - io_uring feature playground — kind
extraMount, DaemonSet installer, and curated profile