Suprnova ships two CLI commands that generate Docker artifacts you can
adopt verbatim or modify. docker:init writes a multi-stage Dockerfile
.dockerignorefor production.docker:composewrites adocker-compose.ymlfor local development services (database, cache, and optionally Mailpit + MinIO). Both commands write into the current project root; neither tries to drive your container runtime.
docker:init
Generate a production Dockerfile alongside a matching .dockerignore.
The command refuses to overwrite an existing Dockerfile; remove the
existing file first if you want to regenerate.
What gets written
| File | Purpose |
|---|---|
Dockerfile |
Three-stage build: frontend assets, Rust release binary, runtime image |
.dockerignore |
Excludes target/, node_modules/, .env*, the existing build artifacts, and the Docker files themselves |
Dockerfile shape
The generated Dockerfile uses three stages so the runtime image carries only the compiled binary plus its required shared libraries:
frontend-builder-node:20-alpine. Installs npm deps and runsnpm run build, producingfrontend/dist.backend-builder-rust:1.91.1-slim-bookworm. CachesCargo.tomlCargo.lockas a dependency layer, then copies yourcmd/,src/, and the builtfrontend/dist(aspublic/assets) and runscargo build --release.
runtime-debian:bookworm-slimwithca-certificatesandlibssl3. Runs as a non-rootappuser. Copies the binary in as./appand thepublic/directory beside it. Exposes port 8765.
The final image's default CMD is ["./app"], which runs the unified
binary's serve subcommand (web server with auto-migrations on
startup). To run a different subcommand, override the command at
docker run time:
# Web server (default)
# Run migrations only and exit
# Run the scheduler daemon
# Run the queue worker
Pass production config through --env-file .env.production or
individual -e flags. .env.production should never be committed -
it's already covered by the .dockerignore.
Bumping the Rust toolchain
The Dockerfile pins rust:1.91.1-slim-bookworm for the build stage so a
freshly-generated image is reproducible and matches Suprnova 0.6's declared
MSRV. Custom Dockerfiles should use the same or a newer toolchain:
FROM rust:1.91.1-slim-bookworm AS backend-builder
Pin to whatever toolchain version matches what rust-toolchain.toml (if
you have one) or your local rustc --version reports.
Why Suprnova diverges
Laravel deployments typically run multiple processes per container or host: php-fpm for web, a queue worker, a scheduler, sometimes a Horizon dashboard, sometimes an Octane runner. Each one is its own service definition.
Suprnova compiles to one statically-linked binary that knows every
subcommand the framework ships - serve, migrate, queue:work,
schedule:work, workflow:work, ssr:start. The same Docker image
runs every role; the only thing that changes is the command. That makes
"web + worker + scheduler" three services in your orchestrator that all
point at the same image tag - one build to roll the entire app forward.
docker:compose
Generate a docker-compose.yml that brings up local development
services.
Like docker:init, this refuses to overwrite an existing
docker-compose.yml. It also appends docker-compose.override.yml to
your .gitignore (if a .gitignore is present) so you can keep
per-developer overrides locally without committing them.
Options
| Option | Description |
|---|---|
--with-mailpit |
Include the Mailpit email-testing service |
--with-minio |
Include MinIO (S3-compatible object storage) |
If you pass neither flag, the command prompts interactively for both. Passing either flag skips the prompt and uses the flag values you gave.
What you always get
PostgreSQL and Redis are written into every generated compose file:
| Service | Default port | Image |
|---|---|---|
| PostgreSQL | 5432 | postgres:16-alpine |
| Redis | 6379 | redis:7-alpine |
Both services have health checks, persistent named volumes, and live on
a project-scoped network (<project>_network). The Postgres user,
password, and database default to suprnova / suprnova_secret /
suprnova_db.
Optional services
When you opt in:
| Service | Default ports | Image |
|---|---|---|
| Mailpit | 1025 (SMTP), 8025 (UI) | axllent/mailpit:latest |
| MinIO | 9000 (S3 API), 9001 (Console) | minio/minio:latest |
Mailpit defaults to accepting any SMTP auth so you don't have to
configure credentials during development; the web UI at
http://localhost:8025 shows every email your app sends. MinIO's
default credentials are minioadmin / minioadmin.
Running the stack
# Bring everything up in the background
# Tail logs
# Stop and remove the containers (volumes persist)
# Remove volumes too (wipes the local database)
Wiring .env to compose
The compose file uses ${VAR:-default} syntax everywhere, so you can
override anything by setting it in .env or your shell. A typical
.env for the default stack:
DATABASE_URL=postgres://suprnova:suprnova_secret@localhost:5432/suprnova_db
REDIS_URL=redis://localhost:6379
# Mailpit (if enabled)
MAIL_DRIVER=smtp
MAIL_HOST=localhost
MAIL_PORT=1025
# MinIO (if enabled)
FILESYSTEM_DISK=s3
S3_ENDPOINT=http://localhost:9000
S3_ACCESS_KEY=minioadmin
S3_SECRET_KEY=minioadmin
S3_BUCKET=local
S3_REGION=us-east-1
To override a port (e.g. because 5432 is already in use), set the matching env var before bringing the stack up:
DB_PORT=5433
The full set of overridable ports:
| Variable | Service | Default |
|---|---|---|
DB_PORT |
PostgreSQL | 5432 |
REDIS_PORT |
Redis | 6379 |
MAILPIT_SMTP_PORT |
Mailpit SMTP | 1025 |
MAILPIT_UI_PORT |
Mailpit UI | 8025 |
MINIO_API_PORT |
MinIO S3 | 9000 |
MINIO_CONSOLE_PORT |
MinIO Console | 9001 |
Customising the compose file
docker-compose.yml is yours to edit after generation - Suprnova
doesn't regenerate or read it later. Common patches:
- Swap
postgres:16-alpineformysql:8ormariadb:11if you prefer one of those drivers; both are first-class in Suprnova - Add a
volumes:entry that mounts yourmigrations/directory if you want to run migrations inside a one-shot container - Add additional services (Qdrant, Elasticsearch, Nats) the same way
Production deployment
For a real deployment, run docker:init and treat the generated
Dockerfile as your build input. Most orchestrators (Railway, Fly,
Digital Ocean App Platform, Kubernetes) just need three things:
- The image tag built from this
Dockerfile - An env file with
DATABASE_URL,APP_KEY, and any driver-specific keys - A health check pointing at
GET /_suprnova/health/live(and, if the platform distinguishes the two, a readiness check at/_suprnova/health/ready)
The single-binary shape means every role uses the same image; you
declare a "web" service running ./app and a "scheduler" or "worker"
service running ./app schedule:work (or ./app queue:work). Both
read the same env, so they stay in lockstep on every deploy.
See Deployment for the platform-agnostic checklist, and the platform guides for fully-worked examples: Railway, Digital Ocean, Hetzner VPS.
Summary
| Command | Writes | When to use |
|---|---|---|
suprnova docker:init |
Dockerfile, .dockerignore |
Building production images |
suprnova docker:compose |
docker-compose.yml |
Bringing up local Postgres/Redis/Mailpit/MinIO |
Next
- Deployment - the platform-agnostic deployment checklist
- Railway - managed PaaS with build-from-git
- Digital Ocean - App Platform deploys
- Hetzner VPS - bare-metal with systemd + Caddy
- Environment Variables - every key the framework reads
