Deployment
Orchka needs four things running: the web service, the worker, a Postgres database, and a Redis instance. The worker is not optional — it is where every workflow actually executes.
Deploy the worker as an always-on background process with no port and no HTTP health check. It polls Redis and never serves HTTP, so a platform that sleeps idle services, or one that requires an HTTP check, will stop it working.
Docker
Two images, built from the repo root:
docker build -f Dockerfile.web -t orchka-web .
docker build -f Dockerfile.worker -t orchka-worker .Or run the whole stack, including Redis:
docker compose --profile app up -dThe app services sit behind an app profile, so docker compose up -d redis
still starts infrastructure only for local development.
Both images carry Bun and Node: Bun runs the workspace, but next and
prisma ship Node shebangs. They also install OpenSSL, which Prisma's query
engine links against.
Migrations
Run them once, as a release or pre-deploy step, before the new version takes traffic:
cd packages/db && bunx prisma migrate deployDo not run migrations at build time. On most platforms the build step cannot
reach the managed database — private networking is runtime-only. (prisma generate at build is fine; it needs DATABASE_URL to be set, not reachable.)
The Prisma client is generated during the image build, not committed, so a fresh
clone needs bun run prisma:generate before it will build locally.
Render
A blueprint is committed at render.yaml. Dashboard → New → Blueprint,
point it at the repo, and it proposes all four components.
CREDENTIAL_ENCRYPTION_KEY is declared in a shared environment group, so you
enter it once and both services receive the same value — a mismatch between them
is silent and makes credentials undecryptable. BETTER_AUTH_SECRET is
generated for you.
After the first deploy, set BETTER_AUTH_URL to the assigned URL and register
that callback in your GitHub and Google OAuth apps.
DigitalOcean App Platform
An app spec is committed at .do/app.yaml:
doctl apps create --spec .do/app.yamlSet the secret values in the dashboard afterwards. CREDENTIAL_ENCRYPTION_KEY
must be pasted identically into both the web and worker components — App
Platform has no shared-group equivalent.
BETTER_AUTH_URL is wired to ${APP_URL}, so OAuth callbacks are the only URL
you set by hand.
Check the Valkey eviction policy is noeviction after creation. BullMQ keeps
in-flight job state there; on an evicting policy, jobs are discarded under memory
pressure and workflows vanish mid-run with nothing in the logs.
Anywhere else
The Docker images run on Fly.io, ECS, Kubernetes, or a single VPS with
docker compose. Four things carry over regardless of host:
- The worker gets no port and no HTTP health check.
- Redis must persist and must not evict. See Operations.
- Migrations run pre-deploy, not at build time.
CREDENTIAL_ENCRYPTION_KEYmust be byte-identical across both processes, and backed up somewhere you will still have it after a disaster.
Platforms that cannot run a long-lived background process — most "serverless" hosting — can serve the web half but not the worker.
Before real users
- Set
RESEND_API_KEY. Without it, password reset links are written to the server log rather than emailed, so nobody but you can recover an account. - Decide on email verification. See Security for the address-squatting window it closes.
- Set
ORCHKA_ADMIN_EMAILS, or/settings/engineis inaccessible to everyone. - Arrange Postgres backups and store the encryption key somewhere separate.