kevy4.0

Unix-domain socket (UDS) transport

kevy exposes an optional Unix-domain stream listener that speaks identical RESP semantics to the TCP port, letting same-host clients skip the loopback stack entirely.

When you need this

UDS is the right transport when the client and server share a host:

Cross-host clients still need TCP — UDS is filesystem-scoped and never leaves the kernel.

Core idea

Set KEVY_UNIX_SOCKET to a filesystem path and kevy dual-binds: the TCP listener stays up exactly as before, and a UDS listener accepts on the same shard runtime with the same RESP2/3 parser. Any RESP client that takes a unix:// URL or -s <path> flag switches over with one line of config. UDS eliminates loopback rep_movs, nft_do_chain, and the TCP syscall path, so the per-op floor drops materially on every workload.

Worked example

Start kevy with both transports enabled:

KEVY_UNIX_SOCKET=/tmp/kevy.sock kevy --port 6379

Connect via UDS with redis-cli:

redis-cli -s /tmp/kevy.sock SET foo bar
# OK
redis-cli -s /tmp/kevy.sock GET foo
# "bar"

TCP on :6379 is still live in parallel — same data, same shards:

redis-cli -p 6379 GET foo
# "bar"

The in-tree Rust clients (kevy-client / kevy-client-async) speak tcp:// / kevy:// / redis:// plus the in-process mem:// and file:/// schemes — they do not take a unix:// URL. From Rust, a same-host client either connects over TCP loopback or, when it lives in the same process, skips sockets entirely with the embedded backend (file:/// / mem://), which is faster than UDS could be. UDS is for out-of-process, same-host clients in other languages or ecosystem drivers.

Permissions and security

The trust boundary for UDS is the filesystem — there is no RESP-level AUTH or TLS on the Unix socket. Whoever can open(2) the socket file can issue any command, including FLUSHALL.

Server config knobs

Env varCLI flagDefaultEffect
KEVY_UNIX_SOCKET(env-only for now)unsetFilesystem path to bind. Unset = TCP-only.
KEVY_BIND--bind127.0.0.1TCP bind address; UDS bind is independent.
--port--port6379TCP port; UDS still binds when set.

Notes:

Trade-offs

UDS vs TCP loopback on the same kevy binary:

AspectUDSTCP loopback
Per-op floorlower (no IP/checksum/port/NAGLE)higher
Reachsame host onlyany host
Identityfilesystem permissionsport + bind address + AUTH
Lifecyclesocket file on disk; must be cleaned on restartport lifecycle is kernel-managed
Observabilitylsof / ss -xlss -tln, netstat, tcpdump
Client configunix:///path or -s /pathhost:port

The throughput gain is workload-shape dependent — small-payload low-connection cells gain the most (the loopback per-op tax dominated them); CPU-saturated cells gain less (the transport wasn't the floor). See bench/REPORT.md for measured numbers.

FAQ

Can I bind UDS and TCP at the same time?

Yes — that's the only mode. Setting KEVY_UNIX_SOCKET adds a UDS listener; the TCP listener stays up exactly as it was. Use whichever per-client makes sense.

Server refuses to start — "socket exists"?

Intentional. kevy will not unlink a path it didn't create, because that lets a misconfigured run silently steal another service's socket. Either remove the stale file (rm -f /tmp/kevy.sock) before restart, or use a per-run path like /run/kevy/$(uuidgen).sock. If kevy crashed and left the file behind, removing it manually is safe.

How fast is UDS vs TCP loopback?

Materially faster on every workload, because UDS skips the entire IP path: no checksum, no netfilter chain (nft_do_chain), no rep_movs through loopback, no per-packet ACK round-trip. The exact ratio depends on what fraction of the per-op budget was loopback overhead — single-connection small-payload workloads see the biggest jump; CPU-bound pipelined cells see less. Measure on your workload with redis-benchmark -s /tmp/kevy.sock vs -h 127.0.0.1.

Can my client library use UDS?

Most ecosystem drivers do. redis-cli and redis-benchmark take -s <path>. ioredis, node-redis, redis-py, redis-rb, go-redis, lettuce, and jedis all accept unix:///path URLs or an explicit socket-path option — check your driver's connection-options docs for the exact key name. The in-tree kevy-client / kevy-client-async do not speak UDS: for a Rust client in the same process the embedded file:/// / mem:// backends beat any socket, and across processes they use TCP.

Should I drop TCP entirely if all my clients are on the same host?

You can, but you don't have to. Leaving TCP bound to 127.0.0.1 costs nothing if no one connects, and it leaves a fallback if a client's UDS path gets misconfigured. The usual deployment is "UDS for the hot client, TCP for redis-cli debugging."