grpc / grpc/grpc-rust

Add socket-activation support for grpc.

Open
#2,733 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
12.5k
Forks
1.3k
Avg merge
4d 7h
Merged PRs (30d)
24

Description

## Feature Request

### Crates

`tonic` (transport server: `TcpIncoming` / `UnixIncoming`), behind a new opt-in `socket-activation` cargo feature. Unix-only.

### Motivation

Under systemd, a service can be started on-demand when a client connects. The manager creates and listens on the socket itself, then passes the already-listening file descriptor to the spawned process via the `LISTEN_FDS` / `LISTEN_PID` environment variables.

Consider a gRPC server that receives requests only occasionally. Keeping it running around the clock just to answer the rare request wastes memory and other resources. A better fit for that situation is to let the process run *only while it is actually serving requests*: the service manager (systemd) holds the socket, starts the server on an incoming connection, and the server can stop itself once it goes idle, after which the manager re-activates it on the next connection.

This enables:
- **Lazy startup / on-demand activation** - the server process only runs when there is actual traffic.
- **Zero-downtime restarts** - the listening socket outlives the process, so connections established during a restart are not refused.

Today a tonic server always calls `bind()` itself, so it cannot participate in socket activation.

### Proposal

Add an opt-in `socket-activation` cargo feature (Unix-only). When enabled, `TcpIncoming::bind` and `UnixIncoming::bind` first check whether a matching listening descriptor was passed in via `LISTEN_FDS` / LISTEN_PID`:

- Validate `LISTEN_PID` matches the current process id.
- Scan the inherited descriptors (`SD_LISTEN_FDS_START = 3` onward), verifying each is a listening stream socket.
- Match by requested address (TCP, treating IPv4-mapped and wildcard binds as equal) or by socket path (UDS).
- Adopt the matching fd instead of binding a fresh socket; otherwise fall back to the normal `bind()`.

The feature is **off by default**, so behavior is unchanged unless explicitly enabled.

### Alternatives

**Do it in user code** - the application reads `LISTEN_FDS` and constructs a listener, passing it via `serve_with_incoming`. This works but forces every user to re-implement the `sd_listen_fds` protocol (pid check, fd scanning, socket-type validation, address matching) correctly. Baking it into the transport makes the common case a one-line, feature-gated opt-in.
- **`libsystemd` / `sd-listen-fds` crate dependency** - pulls in an extra (often C-linked) dependency for a small, well-specified protocol. The proposed implementation needs only `socket2`, which tonic already uses.

Contributor guide

Open the contributing guide

Research direction

Start by locating tonic's transport server implementations for TcpIncoming and UnixIncoming, then read how their bind paths use socket2 and how cargo features are declared. Trace the LISTEN_FDS/LISTEN_PID protocol and inherited descriptors, including address or socket-path matching. Done means the opt-in Unix feature adopts a matching listening descriptor, falls back to normal bind otherwise, and leaves default behavior unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend, networking
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.