Configure production providers
Connect production storage, caches, databases, jobs, and telemetry without confusing resources with application hosting.
Choose runtime resources independently from the application host. An AWS-hosted app can use an existing Redis service or Cloudflare R2; selecting those adapters does not make the deployment stack own their lifecycle.
Pre-setup
Begin with local Docker configuration and passing module checks. Before releasing, obtain the target resource’s connection values and store secrets in your deployment’s runtime environment. Keep stable descriptor IDs and profile names when changing their provider. Verify supported operations: a connected provider is not automatically equivalent to the one used during development.
Application fields belong in defineEnv. The named binding references below describe provider
connections; they are not extra context.env fields. Keep your existing environment contract,
server settings, and application identity when merging either example.
Buckets and caches
Keep Docker as the local default, with explicit connected values available for release builds. Install the owners you select; this complete example uses both:
From this guide’s application/package root, install only its selected requirements:
bun add @relkit/docker@0.7.12 @relkit/s3@0.7.12Keep these owner-specific versions and the lockfile together. Runtime SDKs belong in dependencies; build/type tooling belongs in devDependencies. Installing an adapter does not supply its optional SDK peers or enable the feature’s configuration.
From this guide’s application/package root, install only its selected requirements:
bun add @relkit/docker@0.7.12 @relkit/redis@0.8.1Keep these owner-specific versions and the lockfile together. Runtime SDKs belong in dependencies; build/type tooling belongs in devDependencies. Installing an adapter does not supply its optional SDK peers or enable the feature’s configuration.
/** Local Docker recipes pair with explicit connected resource values for release builds. */
import { defineApp, defineEnv, env as binding } from "@relkit/app/config";
import { docker } from "@relkit/docker";
import { redis } from "@relkit/redis";
import { s3 } from "@relkit/s3";
export default defineApp({
env: defineEnv({}),
bucket: {
assets: docker(
s3({
endpoint: binding.url("ASSETS_S3_ENDPOINT"),
bucketName: binding.string("ASSETS_S3_BUCKET"),
region: binding.string("ASSETS_S3_REGION"),
credentials: {
accessKeyId: binding.secret("ASSETS_S3_ACCESS_KEY_ID"),
secretAccessKey: binding.secret("ASSETS_S3_SECRET_ACCESS_KEY"),
},
forcePathStyle: true,
}),
),
},
cache: { requests: docker(redis({ url: binding.secret("REQUESTS_REDIS_URL") })) },
defaults: { bucket: "assets", cache: "requests" },
});Set ASSETS_S3_ENDPOINT, ASSETS_S3_BUCKET, ASSETS_S3_REGION, the two S3 credential values,
and REQUESTS_REDIS_URL in the production process environment. The resources must already exist
and be reachable by the server. Adjust path-style access for the selected S3 service.
Docker overrides local connection values during development; it does not provision production resources.
For stack-owned resources, use AWS wrappers and review their options. AWS can own supported S3 and Redis/Valkey resources; a direct connected adapter never requests that lifecycle.
Cloudflare has no bundled Docker recipe. Keep MinIO/Redis for ordinary local development, then explicitly select this configuration when testing or releasing against existing R2/KV resources:
From this guide’s application/package root, install only its selected requirements:
bun add @relkit/cloudflare@0.7.12Keep these owner-specific versions and the lockfile together. Runtime SDKs belong in dependencies; build/type tooling belongs in devDependencies. Installing an adapter does not supply its optional SDK peers or enable the feature’s configuration.
/** Connect existing R2 and KV resources without selecting Cloudflare application hosting. */
import { defineApp, defineEnv, env as binding } from "@relkit/app/config";
import { kv, r2 } from "@relkit/cloudflare";
export default defineApp({
env: defineEnv({}),
bucket: {
assets: r2({
accountId: binding.string("CLOUDFLARE_ACCOUNT_ID"),
bucketName: binding.string("R2_BUCKET_NAME"),
credentials: {
accessKeyId: binding.secret("R2_ACCESS_KEY_ID"),
secretAccessKey: binding.secret("R2_SECRET_ACCESS_KEY"),
},
}),
},
cache: {
requests: kv({
accountId: binding.string("CLOUDFLARE_ACCOUNT_ID"),
namespaceId: binding.string("KV_NAMESPACE_ID"),
apiToken: binding.secret("CLOUDFLARE_API_TOKEN"),
}),
},
defaults: { bucket: "assets", cache: "requests" },
});Supply the account ID, bucket name, namespace ID, and three credential values named in the example. Use credentials scoped to the intended resources. R2 supports signed URLs; KV requires a TTL of at least 60 seconds and does not support atomic increment. Do not reuse a Redis counter/rate-limit cache without checking those limits. See R2 and KV reference.
This connects REST/S3-compatible runtime APIs from the Bun server. It does not deploy the application to Cloudflare Workers or create the bucket/namespace.
Realtime and agent checkpoints
Use the Redis realtime configuration with a reachable secret
connection binding in production, and configure agent-state separately for agents. Confirm Redis
persistence and shared fanout across replicas; local filesystem profiles require one shared,
appropriately managed state location and are not a replacement for distributed provider semantics.
Durable job providers
| Provider | Local default | Deployment guidance |
|---|---|---|
| Inngest | docker(inngest()) |
Inngest setup: connected credentials, native worker registration, and capability evidence |
| Trigger.dev | docker(trigger()) |
Trigger.dev setup: target runtime, immutable worker publication, and provider connection |
| Effect MQ | docker(effectMq()) |
Effect MQ setup: PostgreSQL-backed native execution and worker lifecycle |
Follow job deployment to publish workers, verify readiness, and activate schedules. Keep previous worker generations while they own accepted runs. Application events use the filesystem provider; persist their state explicitly.
Database and authentication
The database service connects PostgreSQL, MySQL, or SQLite.
RELKIT does not provision an application database through defineApp. Supply its connection via
defineEnv, migrate once before rollout, and keep backups and connection/network policy with the
database owner. The Docker PostgreSQL walkthrough is development setup, not a managed cloud database.
Better Auth shares that service. Use the public production origin
for BETTER_AUTH_URL, a stable secret across replicas, and reviewed cookie/origin policy. Keep
credentials and session tables out of generated public CRUD endpoints.
Models and telemetry
Selected model SDKs own model credentials and availability. Sentry and OTLP are independent telemetry exporters, configured with named endpoint/secret bindings. AWS hosting supplies CloudWatch stdout routing; it does not create a model account, Sentry project, or OTLP service.
Verify the release configuration
Run bun run check, bun run typecheck, and bun run build after changing bindings. These
validate the application and selected contracts; they do not prove remote credentials or network
access. Verify a real read/write and applicable rejection path against your intended provider in
an isolated environment before accepting traffic. Follow AWS deployment
only when that host is selected.