A lightweight, production-grade Python server that speaks both WSGI and ASGI β with full WebSocket support baked in.
minicorn gives you a Uvicorn/Gunicorn-like CLI experience with zero heavyweight dependencies, serving everything from classic Flask apps to modern async FastAPI services over the same port.
| π WSGI | Full PEP 3333 compliance β Flask, Django, and any WSGI app |
| β‘ ASGI | Async support for FastAPI, Starlette, and ASGI 3.0 apps |
| π WebSockets | RFC 6455 compliant WebSocket handling in ASGI mode |
| π Auto-reload | File-watching hot reload for development |
| π οΈ Simple CLI | One command to run any app, WSGI or ASGI |
| π¦ Zero Core Deps | Stdlib only β watchdog needed only for --reload |
| π Structured Logging | Colored, leveled log output |
pip install minicorn
# With dev/reload support
pip install "minicorn[dev]"# Run a Flask or Django app
minicorn main:app
# Custom host and port
minicorn main:app --host 0.0.0.0 --port 8080
# Hot reload during development
minicorn main:app --reloadAdd --asgi to switch to async mode:
# Run a FastAPI app
minicorn main:app --asgi
# With hot reload
minicorn main:app --asgi --reload
# Custom port
minicorn main:app --asgi --port 8080WebSocket support is built into ASGI mode β no extra flags needed.
Any endpoint that upgrades to WebSocket (HTTP 101) is handled automatically.
minicorn main:app --asgi
# ws://127.0.0.1:8000/ws is ready to accept connectionsEnable keepalive pings to detect dead connections:
minicorn main:app --asgi --ws-ping-interval 20 --ws-ping-timeout 10python -m minicorn main:app --reload
python -m minicorn main:app --asgi --reload# main.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello from minicorn! π₯"minicorn main:app --reload# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello from FastAPI!", "server": "minicorn-asgi"}minicorn main:app --asgi --reload# main.py
from fastapi import FastAPI, WebSocket
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Echo: {data}")minicorn main:app --asgi
# Connect to ws://127.0.0.1:8000/ws# WSGI
from minicorn import serve, run
serve("main:app", host="0.0.0.0", port=8080)
# or pass the app object directly
from myapp import app
run(app, host="127.0.0.1", port=8000)# ASGI
from minicorn import serve_asgi, run_asgi
serve_asgi("main:app", host="0.0.0.0", port=8080)
from myapp import app
run_asgi(app, host="127.0.0.1", port=8000)minicorn --help
--reload works for both WSGI and ASGI. minicorn watches .py files in your project and restarts the server on changes.
minicorn main:app --reload # WSGI
minicorn main:app --asgi --reload # ASGIAutomatically ignored: __pycache__, .git, venv, .venv, node_modules, dist, build, .mypy_cache, .pytest_cache
| Capability | WSGI | ASGI |
|---|---|---|
| HTTP/1.0 & HTTP/1.1 | β | β |
| Keep-Alive connections | β | β |
| Chunked transfer encoding | β | β |
| Streaming responses | β | β |
| WebSocket (RFC 6455) | β | β |
| WebSocket ping/pong | β | β |
| asyncio-based concurrency | β | β |
| Auto-reload | β | β |
| Setting | Default | Description |
|---|---|---|
| Host | 127.0.0.1 |
Bind address |
| Port | 8000 |
Bind port |
| Max Header Size | 64 KB | Reject oversized headers |
| Max Body Size | 1 MB | Reject oversized bodies |
| Recv Timeout | 10s | Timeout waiting for request data |
| Keep-Alive Timeout | 15s | Idle timeout between requests |
| Max Keep-Alive Requests | 100 | Max requests per connection |
| WS Max Message Size | 16 MB | Maximum WebSocket message size |
| WS Ping Interval | disabled | Ping clients every N seconds |
| WS Ping Timeout | disabled | Close if pong not received in N seconds |
MIT License
Contributions are welcome! Please feel free to submit a Pull Request.