Deploy on Vercel
Vercel can run the complete Doso web and API tier. The lowest-change path uses Vercel Services: Next.js for the web tier, the existing Rust API as an OCI container image, and a Marketplace Postgres database.
Recommended architecture#
Use one Postgres cluster with separate databases or schemas, or separate
clusters when stronger isolation is required. Only the web service receives
public traffic. Its existing /v1/** route proxies to the API over the
DOSO_API_PROXY_TARGET service binding.
Prerequisites#
- A Vercel plan/account with Services and Container Images enabled.
- A custom domain such as
app.example.com. - Hosted Postgres with TLS.
- A stable 32-byte settings encryption key.
- Docker for local
vercel devcontainer testing.
Database#
Vercel no longer operates a first-party Postgres product. Install a provider from the Vercel Marketplace:
- Neon is the default recommendation. Its native integration injects a
pooled
DATABASE_URL, supports preview branches, and supplies a direct URL for migrations. - Supabase is also supported. Use the session pooler for the long-lived container path; transaction mode may require disabling prepared statements.
- PlanetScale Postgres works through a normal TLS connection string, but the current Vercel PlanetScale Marketplace listing is MySQL-oriented. Create the Postgres database in PlanetScale and add its pooled URL manually.
Keep runtime and database in the same region. Use the pooled endpoint for application traffic and a direct endpoint for schema migrations.
Deploy the Services configuration#
The example vercel.json defines:
web: the Next.js application inweb/;api: the OCI image built byDockerfile.api.vercel;- a private binding that injects the API URL as
DOSO_API_PROXY_TARGET; - public routing to
webonly.
Run from the repository root:
1vercel link2vercel env add DATABASE_URL3vercel env add BETTER_AUTH_DATABASE_URL4vercel env add BETTER_AUTH_SECRET5vercel env add DOSO_SETTINGS_KEY_HEX6vercel deployFor Git deployments, copy the example configuration to the repository root as
vercel.json, or point your deployment tooling at it with the Vercel CLI local
configuration option. Do not set Vercel's project Root Directory to this
folder: both web/ and the Rust workspace must remain in the build context.
Environment#
Set these for Production, Preview, and Development as appropriate:
1DATABASE_URL=postgresql://...-pooler.../doso?sslmode=require2BETTER_AUTH_DATABASE_URL=postgresql://...-pooler.../auth?sslmode=require3BETTER_AUTH_SECRET=<random 32-byte secret>4DOSO_SETTINGS_KEY_HEX=<64 hex characters>5 6DOSO_AUTH_MODE=jwks7NEXT_PUBLIC_DOSO_AUTH_MODE=session8NEXT_PUBLIC_API_URL=9BETTER_AUTH_URL=https://app.example.com10DOSO_AUTH_ISSUER=https://app.example.com11DOSO_AUTH_AUDIENCE=https://app.example.com12DOSO_PUBLIC_API_URL=https://app.example.com13DOSO_JWKS_URL=https://app.example.com/api/auth/jwks14 15DOSO_POSTGRES_MAX_CONNECTIONS=316DOSO_HOSTED_RUNTIME=trueThe service binding supplies DOSO_API_PROXY_TARGET; do not hardcode it.
Runtime limits#
Vercel Container Images run as Functions:
- idle production instances scale down after five minutes;
- containers receive
SIGTERMwith a 30-second shutdown grace period; - Rust/container requests are limited to 300 seconds on Hobby and 800 seconds on Pro/Enterprise;
- request and response bodies are limited to 4.5 MiB;
- instances scale horizontally automatically;
- local files are ephemeral and cannot hold SQLite, the settings key, or the Git vault.
The existing API can serve normal requests and SSE from a container, but multiple replicas are not yet a safe production topology. Run events and live settings are process-local, startup recovery is not owner-aware, and detached ingestion can outlive an invocation. Large uploads should go directly to object storage and then be imported by URI.
Treat this recipe as a constrained deployment until Doso has durable jobs, run-owner leases, one-shot release migrations, and shared event delivery.
Native Rust Functions#
Vercel's Rust Runtime is a real beta option, but it is not the existing binary packaged differently. A native port must:
- extract the Axum router from process startup;
- wrap it with
vercel_runtimeand cache initialization per warm instance; - remove listener and process-lifetime assumptions;
- move migrations and detached jobs out of handler startup;
- replace local executables/filesystem features such as Poppler and Git;
- solve the same upload, event, duration, and replica-ownership constraints.
Use the OCI image path today; evaluate native Rust Functions when smaller images and function-level isolation justify that application port.
SaaS#
Skip this. Multi-org is ../../saas.
Verify and operate#
- Run migrations against the direct database URL before promotion.
- Check
/api/auth/jwks, sign-in, and an authenticated/v1/settings. - Start a streamed answer, then verify reconnect behavior.
- Replace the API instance and confirm settings remain decryptable.
- Use Vercel Runtime Logs and Observability for container stdout/stderr.
- Promote a validated preview rather than rebuilding production.
Official references: Container Images, Services, Service bindings, Function limits, and Postgres on Vercel.