Deployment¶
Generated projects answer "how do I put this in front of someone?" with one command per situation. All of it is optional — say no to the Docker question and none of this is generated.
The Docker stack¶
uv run opk stack up # build + start; open http://localhost:8080
uv run opk stack ps # services and health
uv run opk stack logs # follow logs (optionally: stack logs backend)
uv run opk stack down # stop (add --volumes to wipe data)
What runs (see the generated docker-compose.yml):
| Service | Role |
|---|---|
web |
nginx: serves the built React UI, proxies /api (and /docs) to the backend |
backend |
the FastAPI app, health-checked |
db |
PostgreSQL 16 with a persistent volume — only if you chose PostgreSQL |
tunnel |
Cloudflare quick tunnel — only with stack share, profile share |
The images build from the repo itself (apps/backend/Dockerfile,
apps/frontend/Dockerfile); a .dockerignore keeps secrets (.env,
license keys) and local state out of the build context. Runtime configuration
comes from your .env via env_file, so licensing and marketplace settings
work in containers the same way they do locally.
Share a public URL in one command¶
This starts the same stack plus cloudflared
in quick tunnel mode — no Cloudflare account, no DNS setup — and prints the
public URL when the tunnel is up. Use it to test on a phone, demo to a client,
or share a work-in-progress. The URL is temporary and unauthenticated:
anyone with the link reaches your stack until stack down, so treat it as
a demo tool, not hosting.
SQLite or PostgreSQL¶
The Copier database question decides what the deployed stack runs:
- SQLite (default): the backend container keeps its database on a named volume. Zero moving parts; right for desktop-first products and small deploys.
- PostgreSQL: the stack gains a
dbservice (exposed onlocalhost:5432, credentialsapp/app) and the backend image includespsycopg. Local dev and tests still use SQLite out of the box; pointAPP_DATABASE_URLat any PostgreSQL when you want to develop against it (the.env.exampleshows the URL). SQLModel + Alembic migrations run unchanged on both.
Going to production¶
The stack is a solid single-server deployment: copy the repo to a VM with
Docker, put your real .env next to it, opk stack up. From there:
- TLS / domain: put your host's nginx/Caddy/Traefik (or a Cloudflare
named tunnel) in front of port 8080, or edit
apps/frontend/nginx.conf— it is a plain nginx site config, also usable outside Docker. - Single container: the backend serves the built web UI at
/wheneverapps/frontend/distexists (APP_WEB_DISToverrides the path), so the backend image alone can deliver the whole product if you prefer one container over the nginx split. - Secrets: never bake
.envinto images (the.dockerignorealready refuses); pass it at runtime. - Accounts: anything public wants
APP_AUTH_ENABLED=true— see Auth & users.
Host-specific walkthroughs — Fly.io, Railway, Cloud Run + Firebase Hosting, static frontend hosts, named Cloudflare tunnels — live in Deploy recipes.