Deployment
Oxide compiles to a single statically-linked binary. Deploy it the same way as any other Rust binary — a Docker container, a bare server, or a Kubernetes deployment. The framework handles SIGTERM cleanly and drains in-flight requests before exiting.
Production knobs
Set these in your deployment’s environment (compose.yml, Kubernetes manifest, systemd unit, etc.). Defaults are sensible but worth reviewing.
| Env var | Default | Notes |
|---|---|---|
APP_LISTEN | 127.0.0.1:8000 | typically 0.0.0.0:<port> behind a load balancer |
APP_BODY_MAX_MB | 10 | raise for file-upload endpoints, lower for API-only services |
APP_REQUEST_TIMEOUT_SECS | 30 | keep below your ingress / load balancer timeout |
APP_SHUTDOWN_TIMEOUT_SECS | 30 | keep below your orchestrator’s grace period (K8s default is 30s) |
DB_CONNECTION | sqlite | set mysql in prod |
BCRYPT_ROUNDS | 12 | OWASP-accepted; don’t lower |
RUST_LOG | info | info,sqlx=warn in prod to cut the query-log noise |
Graceful shutdown
On SIGTERM / SIGINT, Oxide:
- Stops accepting new connections.
- Tells every in-flight connection to finish its current request.
- Waits up to
APP_SHUTDOWN_TIMEOUT_SECSfor them to drain. - Force-closes anything still outstanding, logs a warning, exits 0.
Works with docker stop, K8s termination, systemd stop, etc.
TLS
Oxide binds plain TCP. Terminate TLS at your load balancer, ingress controller, or reverse proxy (nginx, Caddy, Cloudflare, Traefik). The server itself does not handle TLS.
Docker
A compose.yml is scaffolded with every new project for dev. For
prod, build a multi-stage image:
# Build stageFROM rust:1-slim AS buildWORKDIR /appCOPY . .RUN cargo build --release
# Runtime stageFROM debian:bookworm-slimRUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*WORKDIR /appCOPY --from=build /app/target/release/your-app-name /usr/local/bin/appCOPY config/ /app/config/COPY .env.production /app/.envEXPOSE 8000CMD ["app", "serve"]Replace your-app-name with your binary (matches Cargo.toml’s
[[bin]].name).
Health checks
Every scaffolded project ships with:
GET /api/v1/health → 200 "ok"Wire it to your load balancer’s health-check endpoint.
Migrations on deploy
oxide migrate is idempotent — running it every time the app boots is
safe. The default scaffolded main.rs already does that inside
serve():
Migrator::up(db.conn(), None).await.expect("auto-migrate failed on boot");For stricter environments where you want a separate migration step,
remove that line from main.rs and run oxide migrate as a
deployment job before bringing up the server.
Logs
Oxide uses tracing. Everything’s JSON-friendly with
tracing_subscriber::fmt().json():
tracing_subscriber::fmt() .with_env_filter(tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into())) .json() .init();Swap the default init in main.rs for this when you’re ready to ship
to a log aggregator.