Deployment¶
Shipping an Arvel app is mostly the same story as shipping any well-behaved ASGI service: run uvicorn (or another ASGI server) with multiple workers in production, put configuration in the environment, and let a reverse proxy handle TLS and buffering. Arvel does not invent a separate deployment runtime—it gives you FastAPI and Starlette under the hood, plus a CLI for local ergonomics. This page covers production uvicorn, a practical Docker shape, and the environment variables you should treat as non-negotiable outside development.
Running with uvicorn in production¶
arvel serve is optimized for local development (reload, single-process defaults). In production, call uvicorn directly so you control workers, logging, and process management.
From the project root:
uvicorn bootstrap.app:create_app --factory --host 0.0.0.0 --port 8000 --workers 4
Guidelines that tend to age well:
- Disable reload—it is for development only.
- Set workers based on CPU cores (often
2 × cores + 1as a starting point for CPU-bound sync work; profile your own workload). - Terminate TLS at your load balancer or ingress, not necessarily inside the Python process, unless you have a specific reason to end-to-end encrypt inside the cluster.
If the app sits behind nginx, Caddy, or another proxy, enable forwarded headers so Starlette knows the external scheme and client IP. When you used arvel serve, --proxy-headers did that; with raw uvicorn, configure your proxy and ASGI stack consistently (for example trusted proxies / forwarded allow lists) so redirects and absolute URLs stay correct.
Docker deployment¶
A minimal Dockerfile keeps dependencies reproducible and runs uvicorn as the container command. Adjust the Python base image tag to match what your security policy allows; the important part is installing your project (often with uv sync --frozen) and invoking the same ASGI target as production.
FROM python:3.14-slim
WORKDIR /app
# Copy project metadata first for better layer caching
COPY pyproject.toml uv.lock ./
RUN pip install uv && uv sync --frozen --no-dev
COPY . .
ENV APP_ENV=production
EXPOSE 8000
CMD ["uvicorn", "bootstrap.app:create_app", "--factory", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]
Build and run:
docker build -t my-arvel-app .
docker run --rm -p 8000:8000 --env-file .env.production my-arvel-app
Mount secrets with your orchestrator’s mechanism (Kubernetes secrets, ECS task definitions, etc.) rather than baking them into the image.
Environment configuration for production¶
Production should look explicitly different from your laptop:
APP_ENV=production
APP_DEBUG=false
APP_KEY=<long-random-secret>
DB_URL=postgresql+asyncpg://...
CACHE_DRIVER=redis
CACHE_REDIS_URL=redis://redis:6379/0
Checklist:
- Never enable
APP_DEBUGin production—it affects error surfaces and tooling you do not want exposed to end users. - Rotate
APP_KEYwhen compromised; anything derived from it (sessions, signed URLs) must be invalidated consciously. - Use real database and cache URLs from managed services; keep usernames and passwords in a secret store.
- Tune pool settings (
DB_POOL_SIZE, etc.) to match your provider’s connection limits.
Arvel’s Pydantic settings layer means the same code paths read these values in staging and production—what changes is only the data you inject at runtime.
You are ready to run behind uvicorn, package in Docker, and configure like a twelve-factor app. For local setup details, revisit Installation and Configuration.