Production

Running Pounce in production environments

4 min read 787 words
pounce myapp:app \
    --host 0.0.0.0 \
    --port 8000 \
    --workers 0 \
    --log-level warning \
    --log-format json \
    --compression \
    --request-timeout 30 \
    --header-timeout 10 \
    --health-check-path /health \
    --shutdown-timeout 15

Behind a Reverse Proxy

In most production setups, Pounce runs behind a reverse proxy (nginx, Caddy, etc.) that handles TLS termination, static files, and load balancing.

Nginx (TCP)

upstream pounce {
    server 127.0.0.1:8000;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/ssl/cert.pem;
    ssl_certificate_key /etc/ssl/key.pem;

    location / {
        proxy_pass http://pounce;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Request-ID $request_id;

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    # Health check — bypass logging for probe traffic
    location = /health {
        proxy_pass http://pounce;
        access_log off;
    }
}

Nginx (Unix Domain Socket)

Using a Unix socket eliminates TCP overhead and is the recommended approach when nginx and Pounce run on the same host.

pounce myapp:app --uds /run/pounce.sock --workers 0
upstream pounce {
    server unix:/run/pounce.sock;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/ssl/cert.pem;
    ssl_certificate_key /etc/ssl/key.pem;

    location / {
        proxy_pass http://pounce;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Request-ID $request_id;
    }
}

Caddy

example.com {
    reverse_proxy 127.0.0.1:8000
}

Caddy handles TLS automatically via Let's Encrypt.

Proxy Header Trust

When behind a reverse proxy, configuretrusted_hosts so Pounce honours X-Forwarded-For, X-Forwarded-Proto, and X-Forwarded-Hostfrom trusted peers. Without this, proxy headers are stripped for security.

import pounce

pounce.run(
    "myapp:app",
    trusted_hosts=("10.0.0.1",),  # Your proxy's IP
)

Or via CLI:

# Trust is configured via ServerConfig (no CLI flag — intentionally
# requires programmatic configuration for safety)

Warning

Never settrusted_hosts=("*",) in internet-facing deployments. A wildcard trusts every peer, allowing any client to spoof their IP via X-Forwarded-For.

Health Checks

Pounce has a built-in health check endpoint that responds before the ASGI app is invoked — fast, lightweight, and independent of your application.

pounce myapp:app --health-check-path /health

The endpoint returns a JSON payload:

{
    "status": "ok",
    "uptime_seconds": 3600.1,
    "worker_id": 0,
    "active_connections": 42
}

Health check requests are excluded from access logs to reduce noise from Kubernetes probes, load balancer checks, and monitoring agents.

Kubernetes Probes

livenessProbe:
    httpGet:
        path: /health
        port: 8000
    initialDelaySeconds: 5
    periodSeconds: 10
readinessProbe:
    httpGet:
        path: /health
        port: 8000
    initialDelaySeconds: 2
    periodSeconds: 5

Request Tracing

Every request is assigned a uniqueX-Request-ID(UUID4 hex). The ID appears in:

  • Response headersX-Request-IDheader on every response
  • ASGI scopescope["extensions"]["request_id"]for app-level access
  • Access logs — Both text and JSON formats include the request ID

If a trusted proxy sendsX-Request-ID, Pounce uses that value instead of generating a new one. This enables end-to-end tracing across services.

Systemd Service

[Unit]
Description=Pounce ASGI Server
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/.venv/bin/pounce myapp:app \
    --host 0.0.0.0 \
    --workers 0 \
    --health-check-path /health \
    --log-format json
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Unix Socket with Systemd

[Unit]
Description=Pounce ASGI Server
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/.venv/bin/pounce myapp:app \
    --uds /run/pounce.sock \
    --workers 0 \
    --health-check-path /health \
    --log-format json
Restart=always
RestartSec=5
RuntimeDirectory=pounce

[Install]
WantedBy=multi-user.target

Docker

FROM python:3.14t-slim

WORKDIR /app
COPY . .
RUN pip install bengal-pounce[full] .

EXPOSE 8000
HEALTHCHECK CMD curl -f http://localhost:8000/health || exit 1
CMD ["pounce", "myapp:app", "--host", "0.0.0.0", "--workers", "0", "--health-check-path", "/health", "--log-format", "json"]

Graceful Shutdown

Pounce handlesSIGINT and SIGTERM:

  1. Stop accepting new connections
  2. Wait for in-flight requests to complete (up toshutdown_timeout)
  3. Send ASGI lifespan shutdown event
  4. Clean up Unix socket file (if using UDS)
  5. Exit cleanly

Connection Backpressure

When the connection limit is reached (max_connections), Pounce returns an HTTP 503 Service Unavailable response with a Retry-After: 5header instead of silently dropping the connection. This gives clients actionable feedback.

Security Checklist

  • Run behind a reverse proxy for TLS termination
  • Settrusted_hoststo your proxy's IP address
  • Set appropriatemax_request_sizefor your application
  • Use--header-timeoutto protect against slowloris attacks (default: 10s)
  • Use--log-format jsonfor structured logging (parseable by log aggregators)
  • Setserver_headerto a generic value (or empty) to avoid version fingerprinting
  • Use--workers 0for auto-scaled parallelism
  • Enable--health-check-path /healthfor load balancer integration

See Also

  • Security — Proxy headers, CRLF protection, request smuggling
  • Observability — Health checks, request IDs, Prometheus metrics
  • TLS — Direct TLS termination
  • Workers — Worker count tuning
  • ServerConfig — All configuration options