Skip to content

Deployment & Infrastructure

QuadKit applications are ordinary ASGI apps with a clean lifecycle, so they deploy like any modern Python service. This guide covers running, packaging, and configuring for production.

For the full command reference, see the quadkit-cli package.


A QuadKit Application is an ASGI callable (it auto-starts on first request), so you have several ways to run it.

Terminal window
# Auto-detects create_app() and serves with reload
quadkit run
# Pick the entry point, server, and port explicitly
quadkit run my_app.app:create_app --server uvicorn --port 8000

quadkit run supports --host, --port, --workers, --reload/--no-reload, --profile (sets QK_PROFILE), and --server (uvicorn, granian, or hypercorn).

Prefer the same command with production flags. The Dockerfile below does this:

Terminal window
quadkit run --no-reload --host 0.0.0.0 --port 8000 --workers 2

If you already operate an ASGI server, point it at the app object (<app>.app:app) or the factory:

Terminal window
uvicorn my_app.app:app --host 0.0.0.0 --port 8000
# or
granian --interface asgi my_app.app:app

For applications without an HTTP server, run the lifecycle directly — run_application starts the app and shuts down cleanly on SIGINT/SIGTERM:

import asyncio
from quadkit import Application, run_application
from my_app.app import create_app
asyncio.run(run_application(create_app()))

Drive everything through profiles and environment variables — never commit secrets.

Terminal window
export QK_PROFILE=production # loads application.production.yaml over the base
export QK_SQL__BACKEND__URL=postgresql+asyncpg://...
export QK_AUTH__SECRET_KEY=...

Any config key can be overridden with a QK_-prefixed env var using __ for nesting (see YAML Configuration). Set QK_QUIET=true to suppress the startup banner in container logs.


quadkit-web serves a /health endpoint out of the box, and Application provides programmatic probes for orchestrators:

await app.liveness() # is the process alive?
await app.readiness() # ready to serve traffic?
await app.startup_check() # finished booting?
await app.health_check() # aggregated provider health

Wire these to your Kubernetes livenessProbe / readinessProbe.


A multi-stage build with uv keeps images small:

FROM python:3.12-slim-bookworm AS builder
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev
FROM python:3.12-slim-bookworm
WORKDIR /app
COPY --from=builder /app/.venv /app/.venv
COPY . .
ENV PATH="/app/.venv/bin:$PATH"
ENV QK_PROFILE=production
EXPOSE 8000
CMD ["quadkit", "run", "--no-reload", "--host", "0.0.0.0", "--port", "8000"]

services:
api:
build: .
ports:
- "8000:8000"
environment:
QK_PROFILE: development
QK_SQL__BACKEND__URL: postgresql+asyncpg://app:secret_pass@db/app_db
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_DB: app_db
POSTGRES_USER: app
POSTGRES_PASSWORD: secret_pass

  • QK_PROFILE=production and all secrets supplied via env vars.
  • debug: false (validated by config.validate_for_environment(Environment.PRODUCTION)).
  • Database pool sizes and web.server.workers tuned for your instance.
  • Liveness/readiness probes wired to the health endpoints.
  • Migrations applied: quadkit db upgrade.