Skip to content
RELKIT
Esc
↑↓navigate↵open⌘Jpreview
On this page

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.12

Keep 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.1

Keep 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.12

Keep 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.

Reference and source