Skip to content

Commit 730350e

Browse files
authored
Merge pull request #3505 from benoitc/feature/gunicornc-control-interface
feat(ctl): add gunicornc control interface
2 parents 3cba17b + 63df19b commit 730350e

22 files changed

+3859
-20
lines changed

docs/content/2026-news.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
<span id="news-2026"></span>
22
# Changelog - 2026
33

4+
## 25.2.0 - 2026-02-13
5+
6+
### New Features
7+
8+
- **Control Interface (gunicornc)**: Add interactive control interface for managing
9+
running Gunicorn instances, similar to birdc for BIRD routing daemon
10+
- Unix socket-based communication with JSON protocol
11+
- Interactive mode with readline support and command history
12+
- Commands: `show all/workers/dirty/config/stats/listeners`
13+
- Worker management: `worker add/remove/kill`, `dirty add/remove`
14+
- Server control: `reload`, `reopen`, `shutdown`
15+
- New settings: `--control-socket`, `--control-socket-mode`, `--no-control-socket`
16+
- New CLI tool: `gunicornc` for connecting to control socket
17+
- See [Control Interface Guide](guides/gunicornc.md) for details
18+
19+
---
20+
421
## 25.1.0 - 2026-02-12
522

623
### New Features

docs/content/guides/gunicornc.md

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
---
2+
title: Control Interface (gunicornc)
3+
menu:
4+
guides:
5+
weight: 15
6+
---
7+
8+
# Control Interface (gunicornc)
9+
10+
Gunicorn provides a control interface similar to [birdc](https://bird.network.cz/?get_doc&v=20&f=bird-3.html) for the BIRD routing daemon. This allows you to inspect and manage a running Gunicorn instance via a Unix socket.
11+
12+
## Overview
13+
14+
The control interface consists of two parts:
15+
16+
1. **Control Socket Server** - Runs in the arbiter process, accepts commands via Unix socket
17+
2. **gunicornc CLI** - Interactive client that connects to the control socket
18+
19+
## Quick Start
20+
21+
### Start Gunicorn with Control Socket
22+
23+
By default, Gunicorn creates a control socket at `gunicorn.ctl` in the current directory:
24+
25+
```bash
26+
gunicorn -w 4 myapp:app
27+
```
28+
29+
Or specify a custom path:
30+
31+
```bash
32+
gunicorn --control-socket /tmp/myapp.ctl -w 4 myapp:app
33+
```
34+
35+
### Connect with gunicornc
36+
37+
```bash
38+
# Connect to default socket (./gunicorn.ctl)
39+
gunicornc
40+
41+
# Connect to custom socket
42+
gunicornc -s /tmp/myapp.ctl
43+
44+
# Run a single command
45+
gunicornc -c "show workers"
46+
47+
# Output as JSON (for scripting)
48+
gunicornc -c "show stats" -j
49+
```
50+
51+
## Interactive Mode
52+
53+
When run without the `-c` flag, gunicornc enters interactive mode with readline support:
54+
55+
```
56+
$ gunicornc
57+
Connected to gunicorn.ctl
58+
Type 'help' for available commands, 'quit' to exit.
59+
60+
gunicorn> show workers
61+
PID AGE BOOTED LAST_BEAT
62+
----------------------------------------
63+
12345 1 yes 0.2s ago
64+
12346 2 yes 0.1s ago
65+
12347 3 yes 0.3s ago
66+
67+
Total: 3 workers
68+
69+
gunicorn> worker add 2
70+
{
71+
"added": 2,
72+
"previous": 3,
73+
"total": 5
74+
}
75+
76+
gunicorn> quit
77+
```
78+
79+
## Commands
80+
81+
### Show Commands
82+
83+
| Command | Description |
84+
|---------|-------------|
85+
| `show all` | Overview of all processes (arbiter, web workers, dirty workers) |
86+
| `show workers` | List HTTP workers with status |
87+
| `show dirty` | List dirty workers and apps |
88+
| `show config` | Show current effective configuration |
89+
| `show stats` | Show server statistics |
90+
| `show listeners` | Show bound sockets |
91+
| `help` | Show available commands |
92+
93+
### Worker Management
94+
95+
| Command | Description |
96+
|---------|-------------|
97+
| `worker add [N]` | Spawn N workers (default 1) |
98+
| `worker remove [N]` | Remove N workers (default 1) |
99+
| `worker kill <PID>` | Gracefully terminate specific worker |
100+
101+
### Dirty Worker Management
102+
103+
| Command | Description |
104+
|---------|-------------|
105+
| `dirty add [N]` | Spawn N dirty workers (default 1) |
106+
| `dirty remove [N]` | Remove N dirty workers (default 1) |
107+
108+
!!! note "Per-App Worker Limits"
109+
When using `dirty add`, workers only load apps that haven't reached their
110+
worker limits. If all apps are at their limits, no new workers will be spawned.
111+
The response will include a `reason` field explaining this.
112+
113+
### Server Control
114+
115+
| Command | Description |
116+
|---------|-------------|
117+
| `reload` | Graceful reload (equivalent to SIGHUP) |
118+
| `reopen` | Reopen log files (equivalent to SIGUSR1) |
119+
| `shutdown [graceful\|quick]` | Shutdown server (SIGTERM or SIGINT) |
120+
121+
## Example Session
122+
123+
```
124+
$ gunicornc
125+
Connected to gunicorn.ctl
126+
Type 'help' for available commands, 'quit' to exit.
127+
128+
gunicorn> show all
129+
ARBITER (master)
130+
PID: 12345
131+
132+
WEB WORKERS (4)
133+
PID AGE BOOTED LAST_BEAT
134+
--------------------------------------
135+
12346 1 yes 0.05s ago
136+
12347 2 yes 0.04s ago
137+
12348 3 yes 0.03s ago
138+
12349 4 yes 0.02s ago
139+
140+
DIRTY ARBITER
141+
PID: 12350
142+
143+
DIRTY WORKERS (2)
144+
PID AGE APPS
145+
--------------------------------------------------
146+
12351 1 MLModel
147+
ImageProcessor
148+
12352 2 MLModel
149+
150+
gunicorn> show stats
151+
Uptime: 2h 15m 30s
152+
PID: 12345
153+
Workers current: 4
154+
Workers target: 4
155+
Workers spawned: 6
156+
Workers killed: 2
157+
Reloads: 1
158+
159+
gunicorn> worker add
160+
{
161+
"added": 1,
162+
"previous": 4,
163+
"total": 5
164+
}
165+
166+
gunicorn> dirty add 1
167+
{
168+
"success": true,
169+
"operation": "add",
170+
"requested": 1,
171+
"spawned": 1,
172+
"total_workers": 3,
173+
"target_workers": 3
174+
}
175+
176+
gunicorn> quit
177+
```
178+
179+
## Configuration
180+
181+
### Settings
182+
183+
| Setting | CLI Flag | Default | Description |
184+
|---------|----------|---------|-------------|
185+
| `control_socket` | `--control-socket` | `gunicorn.ctl` | Unix socket path |
186+
| `control_socket_mode` | `--control-socket-mode` | `0o600` | Socket file permissions |
187+
| `control_socket_disable` | `--no-control-socket` | `False` | Disable control socket |
188+
189+
### Example Configuration
190+
191+
```python
192+
# gunicorn.conf.py
193+
bind = "0.0.0.0:8000"
194+
workers = 4
195+
196+
# Control socket settings
197+
control_socket = "/var/run/gunicorn/myapp.ctl"
198+
control_socket_mode = 0o660 # Allow group access
199+
```
200+
201+
## Scripting
202+
203+
Use the `-j` flag for JSON output when scripting:
204+
205+
```bash
206+
#!/bin/bash
207+
208+
# Get current worker count
209+
workers=$(gunicornc -c "show stats" -j | jq -r '.workers_current')
210+
echo "Current workers: $workers"
211+
212+
# Scale up if needed
213+
if [ "$workers" -lt 8 ]; then
214+
gunicornc -c "worker add $((8 - workers))"
215+
fi
216+
```
217+
218+
## Security
219+
220+
The control socket uses filesystem permissions for access control:
221+
222+
- **Default mode**: `0o600` (owner only)
223+
- **No authentication**: Relies on filesystem permissions
224+
- **Unix socket only**: No TCP/remote access
225+
226+
To allow group access:
227+
228+
```python
229+
control_socket_mode = 0o660
230+
```
231+
232+
To disable the control socket entirely:
233+
234+
```bash
235+
gunicorn --no-control-socket myapp:app
236+
```
237+
238+
## Protocol
239+
240+
The control interface uses a JSON-based protocol with length-prefixed framing:
241+
242+
```
243+
+----------------+------------------+
244+
| Length (4B BE) | JSON Payload |
245+
+----------------+------------------+
246+
```
247+
248+
### Request Format
249+
250+
```json
251+
{
252+
"id": 1,
253+
"command": "show workers"
254+
}
255+
```
256+
257+
### Response Format
258+
259+
```json
260+
{
261+
"id": 1,
262+
"status": "ok",
263+
"data": { ... }
264+
}
265+
```
266+
267+
### Error Response
268+
269+
```json
270+
{
271+
"id": 1,
272+
"status": "error",
273+
"error": "Unknown command: foo"
274+
}
275+
```
276+
277+
## Troubleshooting
278+
279+
### Cannot connect to socket
280+
281+
```
282+
Error: Connection refused
283+
```
284+
285+
- Check that Gunicorn is running
286+
- Verify the socket path is correct
287+
- Check socket file permissions
288+
289+
### Permission denied
290+
291+
```
292+
Error: Permission denied
293+
```
294+
295+
- Check that you have read/write access to the socket file
296+
- The socket is created with mode `0o600` by default (owner only)
297+
298+
### Socket not found
299+
300+
```
301+
Error: No such file or directory
302+
```
303+
304+
- Gunicorn creates the socket relative to the working directory by default
305+
- Use an absolute path with `--control-socket /path/to/socket.ctl`
306+
- Check if `--no-control-socket` was specified

docs/content/news.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
<span id="news"></span>
22
# Changelog
33

4+
## 25.2.0 - 2026-02-13
5+
6+
### New Features
7+
8+
- **Control Interface (gunicornc)**: Add interactive control interface for managing
9+
running Gunicorn instances, similar to birdc for BIRD routing daemon
10+
- Unix socket-based communication with JSON protocol
11+
- Interactive mode with readline support and command history
12+
- Commands: `show all/workers/dirty/config/stats/listeners`
13+
- Worker management: `worker add/remove/kill`, `dirty add/remove`
14+
- Server control: `reload`, `reopen`, `shutdown`
15+
- New settings: `--control-socket`, `--control-socket-mode`, `--no-control-socket`
16+
- New CLI tool: `gunicornc` for connecting to control socket
17+
- See [Control Interface Guide](guides/gunicornc.md) for details
18+
19+
---
20+
421
## 25.1.0 - 2026-02-12
522

623
### New Features

0 commit comments

Comments
 (0)