Skip to content

Control Interface (gunicornc)

Gunicorn provides a control interface similar to birdc for the BIRD routing daemon. This allows you to inspect and manage a running Gunicorn instance via a Unix socket.

Overview

The control interface consists of two parts:

  1. Control Socket Server - Runs in the arbiter process, accepts commands via Unix socket
  2. gunicornc CLI - Interactive client that connects to the control socket

Quick Start

Start Gunicorn with Control Socket

By default, Gunicorn creates a control socket at gunicorn.ctl in the current directory:

gunicorn -w 4 myapp:app

Or specify a custom path:

gunicorn --control-socket /tmp/myapp.ctl -w 4 myapp:app

Connect with gunicornc

# Connect to default socket (./gunicorn.ctl)
gunicornc

# Connect to custom socket
gunicornc -s /tmp/myapp.ctl

# Run a single command
gunicornc -c "show workers"

# Output as JSON (for scripting)
gunicornc -c "show stats" -j

Interactive Mode

When run without the -c flag, gunicornc enters interactive mode with readline support:

$ gunicornc
Connected to gunicorn.ctl
Type 'help' for available commands, 'quit' to exit.

gunicorn> show workers
PID        AGE    BOOTED   LAST_BEAT
----------------------------------------
12345      1      yes      0.2s ago
12346      2      yes      0.1s ago
12347      3      yes      0.3s ago

Total: 3 workers

gunicorn> worker add 2
{
  "added": 2,
  "previous": 3,
  "total": 5
}

gunicorn> quit

Commands

Show Commands

Command Description
show all Overview of all processes (arbiter, web workers, dirty workers)
show workers List HTTP workers with status
show dirty List dirty workers and apps
show config Show current effective configuration
show stats Show server statistics
show listeners Show bound sockets
help Show available commands

Worker Management

Command Description
worker add [N] Spawn N workers (default 1)
worker remove [N] Remove N workers (default 1)
worker kill <PID> Gracefully terminate specific worker

Dirty Worker Management

Command Description
dirty add [N] Spawn N dirty workers (default 1)
dirty remove [N] Remove N dirty workers (default 1)

Per-App Worker Limits

When using dirty add, workers only load apps that haven't reached their worker limits. If all apps are at their limits, no new workers will be spawned. The response will include a reason field explaining this.

Server Control

Command Description
reload Graceful reload (equivalent to SIGHUP)
reopen Reopen log files (equivalent to SIGUSR1)
shutdown [graceful\|quick] Shutdown server (SIGTERM or SIGINT)

Example Session

$ gunicornc
Connected to gunicorn.ctl
Type 'help' for available commands, 'quit' to exit.

gunicorn> show all
ARBITER (master)
  PID: 12345

WEB WORKERS (4)
  PID        AGE    BOOTED   LAST_BEAT
  --------------------------------------
  12346      1      yes      0.05s ago
  12347      2      yes      0.04s ago
  12348      3      yes      0.03s ago
  12349      4      yes      0.02s ago

DIRTY ARBITER
  PID: 12350

DIRTY WORKERS (2)
  PID        AGE    APPS
  --------------------------------------------------
  12351      1      MLModel
                    ImageProcessor
  12352      2      MLModel

gunicorn> show stats
Uptime:           2h 15m 30s
PID:              12345
Workers current:  4
Workers target:   4
Workers spawned:  6
Workers killed:   2
Reloads:          1

gunicorn> worker add
{
  "added": 1,
  "previous": 4,
  "total": 5
}

gunicorn> dirty add 1
{
  "success": true,
  "operation": "add",
  "requested": 1,
  "spawned": 1,
  "total_workers": 3,
  "target_workers": 3
}

gunicorn> quit

Configuration

Settings

Setting CLI Flag Default Description
control_socket --control-socket gunicorn.ctl Unix socket path
control_socket_mode --control-socket-mode 0o600 Socket file permissions
control_socket_disable --no-control-socket False Disable control socket

Example Configuration

# gunicorn.conf.py
bind = "0.0.0.0:8000"
workers = 4

# Control socket settings
control_socket = "/var/run/gunicorn/myapp.ctl"
control_socket_mode = 0o660  # Allow group access

Scripting

Use the -j flag for JSON output when scripting:

#!/bin/bash

# Get current worker count
workers=$(gunicornc -c "show stats" -j | jq -r '.workers_current')
echo "Current workers: $workers"

# Scale up if needed
if [ "$workers" -lt 8 ]; then
    gunicornc -c "worker add $((8 - workers))"
fi

Security

The control socket uses filesystem permissions for access control:

  • Default mode: 0o600 (owner only)
  • No authentication: Relies on filesystem permissions
  • Unix socket only: No TCP/remote access

To allow group access:

control_socket_mode = 0o660

To disable the control socket entirely:

gunicorn --no-control-socket myapp:app

Protocol

The control interface uses a JSON-based protocol with length-prefixed framing:

+----------------+------------------+
| Length (4B BE) |  JSON Payload    |
+----------------+------------------+

Request Format

{
  "id": 1,
  "command": "show workers"
}

Response Format

{
  "id": 1,
  "status": "ok",
  "data": { ... }
}

Error Response

{
  "id": 1,
  "status": "error",
  "error": "Unknown command: foo"
}

Troubleshooting

Cannot connect to socket

Error: Connection refused
  • Check that Gunicorn is running
  • Verify the socket path is correct
  • Check socket file permissions

Permission denied

Error: Permission denied
  • Check that you have read/write access to the socket file
  • The socket is created with mode 0o600 by default (owner only)

Socket not found

Error: No such file or directory
  • Gunicorn creates the socket relative to the working directory by default
  • Use an absolute path with --control-socket /path/to/socket.ctl
  • Check if --no-control-socket was specified