Back to list
cameronapak

bknd-env-config

by cameronapak

A no-build, un-bloated stack built upon Web Standards that feels freeing to use and can be deployed anywhere.

23🍴 2📅 Jan 21, 2026

SKILL.md


name: bknd-env-config description: Use when configuring environment variables for Bknd projects. Covers .env files, secrets management, env injection in config, platform-specific variables, and production security.

Environment Variables Configuration

Configure environment variables for Bknd applications across development and production.

Prerequisites

  • Bknd project initialized (bknd.config.ts exists)
  • Understanding of your deployment target (local, Cloudflare, Vercel, etc.)

When to Use UI Mode

  • Viewing current config via admin panel
  • N/A for environment variables - all done via code/files

When to Use Code Mode

  • Creating .env files
  • Configuring secrets in bknd.config.ts
  • Setting up platform-specific env vars
  • All environment configuration tasks

Code Approach

Step 1: Create .env File

Create .env in project root:

# Database
DB_URL=file:data.db
DB_TOKEN=

# Auth
JWT_SECRET=your-secret-here-min-32-chars

# Server
PORT=3000

# Development
LOCAL=true

Step 2: Inject Env in Config

Access env vars via the env parameter in bknd.config.ts:

import type { CliBkndConfig } from "bknd";

export default {
  app: (env) => ({
    connection: {
      url: env.DB_URL ?? "file:data.db",
      authToken: env.DB_TOKEN,
    },
    auth: {
      jwt: {
        secret: env.JWT_SECRET ?? "dev-secret-change-in-prod",
      },
    },
  }),
} satisfies CliBkndConfig;

The env parameter receives all environment variables loaded from .env files and system environment.

Step 3: Use .dev.vars for Dev Overrides (Optional)

Bknd loads env files in order (later takes precedence):

  1. .env - Base configuration
  2. .dev.vars - Development-specific overrides (Cloudflare style)

Create .dev.vars for local dev overrides:

# .dev.vars - Dev-only, overrides .env
DB_URL=:memory:
JWT_SECRET=dev-only-secret

Common Environment Variables

Database

VariableDescriptionExample
DB_URLDatabase connection URLfile:data.db, libsql://db.turso.io
DB_TOKENLibSQL/Turso auth tokeneyJhbGciOiJFZERTQSIs...

Authentication

VariableDescriptionExample
JWT_SECRETJWT signing secret (min 32 chars)your-very-long-secret-key-here
GOOGLE_CLIENT_IDGoogle OAuth client ID123456.apps.googleusercontent.com
GOOGLE_CLIENT_SECRETGoogle OAuth secretGOCSPX-xxx
GITHUB_CLIENT_IDGitHub OAuth client IDIv1.abc123
GITHUB_CLIENT_SECRETGitHub OAuth secretsecret_xxx

Media/Storage

VariableDescriptionExample
S3_ACCESS_KEYS3/R2 access keyAKIAIOSFODNN7EXAMPLE
S3_SECRET_KEYS3/R2 secret keywJalrXUtnFEMI/K7MDENG/...
S3_ENDPOINTS3-compatible endpointhttps://bucket.s3.region.amazonaws.com
CLOUDINARY_CLOUD_NAMECloudinary cloud namemy-cloud
CLOUDINARY_API_KEYCloudinary API key123456789012345
CLOUDINARY_API_SECRETCloudinary API secretabcdefghijk...

Server

VariableDescriptionDefault
PORTServer port3000
LOCALDisable telemetry-
NODE_ENV / ENVIRONMENTEnvironment modedevelopment

Complete Configuration Example

import type { CliBkndConfig } from "bknd";
import { em, entity, text } from "bknd";

const schema = em({
  posts: entity("posts", { title: text().required() }),
});

export default {
  app: (env) => ({
    // Database
    connection: {
      url: env.DB_URL ?? "file:data.db",
      authToken: env.DB_TOKEN,
    },

    // Production flag
    isProduction: env.NODE_ENV === "production",

    // Pass all secrets to app
    secrets: env,
  }),

  config: {
    data: schema.toJSON(),

    // Auth with env-based secrets
    auth: {
      enabled: true,
      jwt: {
        secret: env.JWT_SECRET,
        issuer: "my-app",
      },
      strategies: {
        password: { enabled: true },
        google: env.GOOGLE_CLIENT_ID ? {
          config: {
            name: "google",
            type: "oidc",
            client: {
              client_id: env.GOOGLE_CLIENT_ID,
              client_secret: env.GOOGLE_CLIENT_SECRET,
            },
          },
        } : undefined,
      },
    },

    // Media with env-based adapter config
    media: {
      enabled: true,
      adapter: {
        type: "s3",
        config: {
          access_key: env.S3_ACCESS_KEY,
          secret_access_key: env.S3_SECRET_KEY,
          url: env.S3_ENDPOINT,
        },
      },
    },
  },
} satisfies CliBkndConfig;

Platform-Specific Configuration

Cloudflare Workers/Pages

Use wrangler.toml for non-secret vars and dashboard for secrets:

# wrangler.toml
[vars]
ENVIRONMENT = "production"

Set secrets via CLI:

npx wrangler secret put JWT_SECRET
npx wrangler secret put DB_TOKEN

Access in config:

import type { CloudflareBkndConfig } from "bknd/adapter/cloudflare";

export default {
  app: (env) => ({
    connection: env.DB,  // D1 binding
    isProduction: env.ENVIRONMENT === "production",
    secrets: env,
  }),
} satisfies CloudflareBkndConfig;

Vercel

Use Vercel dashboard or CLI for env vars:

vercel env add JWT_SECRET production
vercel env add DB_URL production

Or .env.local for local development (auto-loaded by Next.js):

# .env.local
DB_URL=file:data.db
JWT_SECRET=dev-secret

Docker

Pass via docker-compose or -e flag:

# docker-compose.yml
services:
  app:
    environment:
      - DB_URL=file:/data/app.db
      - JWT_SECRET=${JWT_SECRET}
    env_file:
      - .env.production

Generate .env Template

Use CLI to generate env template from your config:

# Output required secrets as template
npx bknd secrets --template --format env

# Save to file
npx bknd secrets --template --format env --out .env.example

This creates a template without actual values, safe for version control.

SyncSecrets Option

Auto-generate .env.example on config changes:

export default {
  syncSecrets: {
    enabled: true,
    outFile: ".env.example",
    format: "env",  // or "json"
  },
  app: (env) => ({ ... }),
} satisfies CliBkndConfig;

Environment-Based Feature Flags

Conditionally enable features based on environment:

export default {
  app: (env) => ({
    connection: { url: env.DB_URL ?? "file:data.db" },
  }),
  config: {
    auth: {
      enabled: true,
      // Only enable OAuth in production (requires secrets)
      strategies: {
        password: { enabled: true },
        google: env.GOOGLE_CLIENT_ID ? {
          config: {
            name: "google",
            type: "oidc",
            client: {
              client_id: env.GOOGLE_CLIENT_ID,
              client_secret: env.GOOGLE_CLIENT_SECRET,
            },
          },
        } : undefined,
      },
    },
    // Only enable S3 media in production
    media: env.S3_ACCESS_KEY ? {
      enabled: true,
      adapter: {
        type: "s3",
        config: {
          access_key: env.S3_ACCESS_KEY,
          secret_access_key: env.S3_SECRET_KEY,
          url: env.S3_ENDPOINT,
        },
      },
    } : {
      enabled: false,
    },
  },
} satisfies CliBkndConfig;

Database Connection Priority

Bknd resolves database connection in order:

  1. --db-url CLI argument
  2. Config file connection.url
  3. --memory flag (uses :memory:)
  4. DB_URL environment variable
  5. Fallback: file:data.db

Verification

Check env loading:

# Server logs show connection source
npx bknd run
# Look for: "Using connection from ..."

Test env injection:

// Temporarily log env in config
app: (env) => {
  console.log("Loaded env:", Object.keys(env));
  return { ... };
},

Verify secrets command:

npx bknd secrets --template

Common Pitfalls

.env Not Loading

Problem: Env vars undefined in config

Fix: Check file location and format:

# .env must be in project root (same level as bknd.config.ts)
ls -la .env

# No quotes around values
DB_URL=file:data.db     # Correct
DB_URL="file:data.db"   # May cause issues

JWT_SECRET Too Short

Problem: Auth fails or warning about weak secret

Fix: Use minimum 32 characters:

# Generate secure secret
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# or
openssl rand -hex 32

Secrets in Version Control

Problem: Committed .env with real secrets

Fix:

# Add to .gitignore
echo ".env" >> .gitignore
echo ".env.local" >> .gitignore
echo ".dev.vars" >> .gitignore

# Remove from git history if committed
git rm --cached .env

Platform Env Not Available

Problem: env.VAR is undefined in deployed app

Fix: Platform-specific setup:

  • Vercel: Add via dashboard or vercel env add
  • Cloudflare: Add via wrangler secret put or dashboard
  • Docker: Check environment: or env_file: in compose

Wrong Fallback in Production

Problem: Using dev defaults in production

Fix: Fail fast instead of fallback:

app: (env) => {
  if (!env.JWT_SECRET && env.NODE_ENV === "production") {
    throw new Error("JWT_SECRET required in production");
  }
  return {
    auth: {
      jwt: { secret: env.JWT_SECRET ?? "dev-only" },
    },
  };
},

DOs and DON'Ts

DO:

  • Use .env.example as template (no real values)
  • Generate JWT_SECRET with crypto-safe randomness
  • Use platform-specific secret management in production
  • Validate required secrets on app start
  • Use syncSecrets to keep .env.example updated

DON'T:

  • Commit .env with real secrets
  • Use weak or short JWT secrets
  • Hardcode secrets in config files
  • Use same secrets across environments
  • Log env vars containing secrets
  • bknd-local-setup - Initial project setup
  • bknd-setup-auth - Configure authentication
  • bknd-oauth-setup - OAuth provider configuration
  • bknd-storage-config - Storage adapter configuration
  • bknd-production-config - Production configuration
  • bknd-deploy-hosting - Deployment options

Score

Total Score

75/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon