0
# Server Management
1
2
Server lifecycle management with the `Server` class for running ASGI applications and the `ServerState` class for shared state across protocol connections.
3
4
## Imports
5
6
```python
7
from uvicorn import Server, Config
8
from uvicorn.server import ServerState, HANDLED_SIGNALS
9
```
10
11
## Capabilities
12
13
### Server Class
14
15
Main server class that manages the ASGI server lifecycle, including startup, shutdown, and the main event loop.
16
17
```python { .api }
18
class Server:
19
"""
20
ASGI server implementation.
21
22
Manages the asyncio event loop, socket binding, protocol instances,
23
signal handling, and graceful shutdown for ASGI applications.
24
"""
25
26
def __init__(self, config: Config) -> None:
27
"""
28
Initialize server with configuration.
29
30
Args:
31
config: Server configuration object
32
33
Attributes set:
34
config: Server configuration
35
server_state: Shared state across protocol connections
36
started: Whether server has started (False initially)
37
should_exit: Signal for graceful shutdown (False initially)
38
force_exit: Signal for immediate shutdown (False initially)
39
last_notified: Timestamp of last notification (0.0 initially)
40
"""
41
42
def run(self, sockets: list[socket.socket] | None = None) -> None:
43
"""
44
Run the server (blocking).
45
46
Creates an event loop and runs the server synchronously until
47
shutdown. This is the main entry point for running the server.
48
49
**NOTE**: This method automatically calls config.load() if not already loaded,
50
so you don't need to call it manually when using run().
51
52
Args:
53
sockets: Optional pre-bound sockets to use instead of binding new ones
54
55
This method:
56
1. Configures logging
57
2. Loads the application configuration (calls config.load() if needed)
58
3. Creates an event loop
59
4. Runs serve() in the event loop
60
5. Handles cleanup on exit
61
"""
62
63
async def serve(self, sockets: list[socket.socket] | None = None) -> None:
64
"""
65
Run the server (async).
66
67
Asynchronous entry point for running the server. Can be used
68
when you need to run the server within an existing event loop.
69
70
**NOTE**: This method automatically calls config.load() if not already loaded,
71
so you don't need to call it manually when using serve().
72
73
Args:
74
sockets: Optional pre-bound sockets to use instead of binding new ones
75
76
This method:
77
1. Loads config if not already loaded (calls config.load() if needed)
78
2. Calls startup()
79
3. Runs main_loop() if startup succeeded
80
4. Calls shutdown()
81
"""
82
83
async def startup(self, sockets: list[socket.socket] | None = None) -> None:
84
"""
85
Start the server.
86
87
**IMPORTANT**: Before calling startup(), you must call config.load() to initialize
88
the configuration. The startup() method requires access to config.lifespan_class
89
and other attributes that are only set during config.load().
90
91
Performs server initialization:
92
- Starts lifespan handler
93
- Binds sockets
94
- Creates asyncio server
95
- Starts accepting connections
96
97
Args:
98
sockets: Optional pre-bound sockets to use
99
100
Sets:
101
started: True if startup succeeds
102
should_exit: True if startup fails
103
104
Raises:
105
AttributeError: If config.load() has not been called before startup()
106
"""
107
108
async def shutdown(self, sockets: list[socket.socket] | None = None) -> None:
109
"""
110
Shutdown the server gracefully.
111
112
Performs graceful shutdown sequence:
113
1. Stops accepting new connections
114
2. Waits for active connections to complete (up to timeout_graceful_shutdown)
115
3. Shuts down lifespan handler
116
4. Closes all remaining connections
117
5. Cancels all background tasks
118
119
Args:
120
sockets: Optional sockets to close
121
122
This method ensures all resources are cleaned up properly.
123
"""
124
125
async def main_loop(self) -> None:
126
"""
127
Main server event loop.
128
129
Runs a periodic tick loop that:
130
- Checks for shutdown signals
131
- Performs periodic maintenance
132
- Monitors connection health
133
134
The loop continues until should_exit is set.
135
"""
136
137
async def on_tick(self, counter: int) -> bool:
138
"""
139
Periodic tick callback.
140
141
Called regularly from main_loop() for periodic tasks.
142
143
Args:
144
counter: Tick counter (increments each tick)
145
146
Returns:
147
True to continue running, False to trigger shutdown
148
"""
149
150
def handle_exit(self, sig: int, frame: types.FrameType | None) -> None:
151
"""
152
Signal handler for shutdown signals.
153
154
Handles SIGINT, SIGTERM, and SIGBREAK (Windows) to trigger
155
graceful shutdown.
156
157
Args:
158
sig: Signal number
159
frame: Current stack frame
160
161
Sets should_exit on first signal, force_exit on second signal.
162
"""
163
164
def capture_signals(self) -> Generator[None, None, None]:
165
"""
166
Context manager for signal handling.
167
168
Sets up signal handlers on entry and restores original handlers
169
on exit. Used internally to manage signal handling during server
170
execution.
171
172
Yields:
173
None
174
175
Example usage (internal):
176
with server.capture_signals():
177
# Server running with signal handlers active
178
...
179
# Original signal handlers restored
180
"""
181
```
182
183
### Server State
184
185
Shared state available across all protocol instances.
186
187
```python { .api }
188
class ServerState:
189
"""
190
Shared state available across all protocol instances.
191
192
Provides coordination and statistics tracking for all active
193
connections and background tasks.
194
"""
195
196
total_requests: int
197
"""Total number of HTTP requests processed since server start."""
198
199
connections: set[asyncio.Protocol]
200
"""
201
Set of active protocol connection instances.
202
203
Each connection is a protocol instance (HTTP or WebSocket) that is
204
currently handling a client connection. Connections add themselves
205
to this set when created and remove themselves when closed.
206
"""
207
208
tasks: set[asyncio.Task[None]]
209
"""
210
Set of active background tasks.
211
212
Background tasks created by protocol handlers for async operations.
213
Tasks add themselves to this set when created and remove themselves
214
when complete.
215
"""
216
217
default_headers: list[tuple[bytes, bytes]]
218
"""
219
Default HTTP headers to include in all responses.
220
221
Headers are stored as byte tuples and added to every HTTP response
222
unless overridden by the application. Includes Server and Date headers
223
if configured, plus any custom headers from config.headers.
224
"""
225
```
226
227
## Constants
228
229
```python { .api }
230
# Signals that the server handles for graceful shutdown
231
HANDLED_SIGNALS: tuple[int, ...]
232
"""
233
Signals handled by the server for graceful shutdown.
234
235
On Unix: (signal.SIGINT, signal.SIGTERM)
236
On Windows: (signal.SIGINT, signal.SIGTERM, signal.SIGBREAK)
237
238
First signal triggers graceful shutdown (should_exit = True).
239
Second signal triggers immediate shutdown (force_exit = True).
240
"""
241
```
242
243
## Usage Examples
244
245
### Basic Server Usage
246
247
```python
248
from uvicorn import Config, Server
249
250
# Define ASGI application
251
async def app(scope, receive, send):
252
assert scope['type'] == 'http'
253
await send({
254
'type': 'http.response.start',
255
'status': 200,
256
'headers': [[b'content-type', b'text/plain']],
257
})
258
await send({
259
'type': 'http.response.body',
260
'body': b'Hello, World!',
261
})
262
263
# Create configuration
264
config = Config(app=app, host="127.0.0.1", port=8000)
265
266
# Create and run server
267
server = Server(config)
268
server.run()
269
```
270
271
### Running Server in Async Context
272
273
```python
274
import asyncio
275
from uvicorn import Config, Server
276
277
async def app(scope, receive, send):
278
# Your ASGI application
279
...
280
281
async def main():
282
config = Config(app=app, host="127.0.0.1", port=8000)
283
server = Server(config)
284
285
# Run server within existing event loop
286
await server.serve()
287
288
# Run with asyncio
289
asyncio.run(main())
290
```
291
292
### Server with Pre-bound Socket
293
294
```python
295
import socket
296
from uvicorn import Config, Server
297
298
# Create and bind socket manually
299
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
300
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
301
sock.bind(("127.0.0.1", 8000))
302
303
# Pass socket to server
304
config = Config(app="myapp:app")
305
server = Server(config)
306
server.run(sockets=[sock])
307
```
308
309
### Graceful Shutdown Handling
310
311
```python
312
import asyncio
313
from uvicorn import Config, Server
314
315
async def app(scope, receive, send):
316
# Your ASGI application
317
...
318
319
async def run_server():
320
config = Config(
321
app=app,
322
host="127.0.0.1",
323
port=8000,
324
timeout_graceful_shutdown=30, # 30 second graceful shutdown
325
)
326
327
# IMPORTANT: Must call load() before creating Server and calling startup()
328
config.load()
329
330
server = Server(config)
331
332
# Start server
333
await server.startup()
334
335
if server.started:
336
try:
337
# Run until interrupted
338
await server.main_loop()
339
finally:
340
# Ensure clean shutdown
341
await server.shutdown()
342
343
asyncio.run(run_server())
344
```
345
346
### Programmatic Shutdown
347
348
```python
349
import asyncio
350
from uvicorn import Config, Server
351
352
async def app(scope, receive, send):
353
# Your ASGI application
354
...
355
356
async def run_with_timeout():
357
config = Config(app=app, host="127.0.0.1", port=8000)
358
config.load() # Required before calling startup()
359
360
server = Server(config)
361
362
# Start server
363
await server.startup()
364
365
if server.started:
366
# Run server for 60 seconds then shutdown
367
try:
368
await asyncio.sleep(60)
369
finally:
370
server.should_exit = True
371
await server.shutdown()
372
373
asyncio.run(run_with_timeout())
374
```
375
376
### Monitoring Server State
377
378
```python
379
import asyncio
380
from uvicorn import Config, Server
381
382
async def app(scope, receive, send):
383
# Your ASGI application
384
...
385
386
async def monitor_server():
387
config = Config(app=app, host="127.0.0.1", port=8000)
388
config.load() # Required before calling startup()
389
390
server = Server(config)
391
392
await server.startup()
393
394
if server.started:
395
# Monitor connections and requests
396
async def stats_reporter():
397
while not server.should_exit:
398
state = server.server_state
399
print(f"Active connections: {len(state.connections)}")
400
print(f"Total requests: {state.total_requests}")
401
print(f"Background tasks: {len(state.tasks)}")
402
await asyncio.sleep(5)
403
404
# Run stats reporter alongside server
405
stats_task = asyncio.create_task(stats_reporter())
406
407
try:
408
await server.main_loop()
409
finally:
410
stats_task.cancel()
411
await server.shutdown()
412
413
asyncio.run(monitor_server())
414
```
415
416
### Custom Signal Handling
417
418
```python
419
import signal
420
import asyncio
421
from uvicorn import Config, Server
422
423
async def app(scope, receive, send):
424
# Your ASGI application
425
...
426
427
async def run_with_custom_signals():
428
config = Config(app=app, host="127.0.0.1", port=8000)
429
config.load() # Required before calling startup()
430
431
server = Server(config)
432
433
# Custom shutdown handler
434
def shutdown_handler(signum, frame):
435
print(f"Received signal {signum}, shutting down...")
436
server.should_exit = True
437
438
# Set up custom signal handlers
439
signal.signal(signal.SIGINT, shutdown_handler)
440
signal.signal(signal.SIGTERM, shutdown_handler)
441
442
await server.startup()
443
444
if server.started:
445
try:
446
await server.main_loop()
447
finally:
448
await server.shutdown()
449
450
asyncio.run(run_with_custom_signals())
451
```
452
453
### Multi-Stage Startup and Shutdown
454
455
```python
456
import asyncio
457
from uvicorn import Config, Server
458
459
async def app(scope, receive, send):
460
# Your ASGI application
461
...
462
463
async def run_with_stages():
464
config = Config(app=app, host="127.0.0.1", port=8000)
465
466
# Stage 0: Load configuration (required before startup)
467
print("Loading configuration...")
468
config.load()
469
470
server = Server(config)
471
472
# Stage 1: Startup
473
print("Starting server...")
474
await server.startup()
475
476
if not server.started:
477
print("Server failed to start")
478
return
479
480
print(f"Server started on {config.host}:{config.port}")
481
print(f"Active connections: {len(server.server_state.connections)}")
482
483
# Stage 2: Run
484
try:
485
await server.main_loop()
486
except KeyboardInterrupt:
487
print("\nInterrupted by user")
488
489
# Stage 3: Shutdown
490
print("Shutting down server...")
491
await server.shutdown()
492
print("Server stopped")
493
494
asyncio.run(run_with_stages())
495
```
496
497
### Server with Connection Limiting
498
499
```python
500
from uvicorn import Config, Server
501
502
async def app(scope, receive, send):
503
# Your ASGI application
504
...
505
506
# Limit concurrent connections
507
config = Config(
508
app=app,
509
host="0.0.0.0",
510
port=8000,
511
limit_concurrency=100, # Max 100 concurrent connections
512
backlog=2048, # Socket backlog
513
)
514
515
server = Server(config)
516
server.run()
517
```
518
519
### Server with Request Limiting
520
521
```python
522
from uvicorn import Config, Server
523
524
async def app(scope, receive, send):
525
# Your ASGI application
526
...
527
528
# Restart after processing 10000 requests
529
config = Config(
530
app=app,
531
host="0.0.0.0",
532
port=8000,
533
limit_max_requests=10000,
534
)
535
536
server = Server(config)
537
server.run()
538
```
539