Deployment

Build an application image, run the compose stack, and ship to production.

Build and run

bash
cargo build --release -p example-app
./target/release/example-app

The compose stack builds the app from the workspace Dockerfile and exposes a /health liveness probe on port 8000.

bash
docker-compose up -d
docker-compose down

Infrastructure services

ServiceImagePortRole
appbuilt from Dockerfile8000HTTP application with /health probe
postgrespgvector/pgvector:pg165432Primary SQL store + pgvector
redisredis:7-alpine6379Cache + queue backend
miniominio/minio9000, 9001S3-compatible object storage (9001 = console)
mailpitaxllent/mailpit1025, 8025SMTP capture + web UI

Production configuration

Two rules to internalize before shipping:

  1. Environment variables always win over TOML files. The config loader merges config/*.toml first, then applies the process environment last — so secrets belong in the environment, not in files.
  2. Destructive migrations are gated. migrate:fresh is refused in production unless --force is passed explicitly.
bash
APP_ENV=production APP_KEY=... DATABASE_URL=... cargo run --release

Run migrations as a release step, then bring the app up:

bash
cargo artisan migrate --force

Background workers

Queue workers and the scheduler are separate processes in production:

bash
cargo artisan queue:work --queue=default
cargo artisan schedule:run

For recurring dispatch, drive schedule:run from an external timer or a long-running supervisor, and use the pause/resume commands during deploys to avoid double dispatch:

bash
cargo artisan schedule:pause
# deploy
cargo artisan schedule:resume

Release checklist

  • cargo xtask ci passes (fmt, clippy, deps, lines, DAG cycles)
  • cargo test --workspace passes
  • cargo deny check and cargo audit are clean
  • APP_KEY and all secrets come from the environment
  • Migrations run with --force and are reversible
  • Health probe on /health matches the orchestrator configuration

Last updated Sep 22, 2026