jeremylongshore

clerk-multi-env-setup

@jeremylongshore/clerk-multi-env-setup
jeremylongshore
1,004
123 forks
Updated 1/18/2026
View on GitHub

Configure Clerk for multiple environments (dev, staging, production). Use when setting up environment-specific configurations, managing multiple Clerk instances, or implementing environment promotion. Trigger with phrases like "clerk environments", "clerk staging", "clerk dev prod", "clerk multi-environment".

Installation

$skills install @jeremylongshore/clerk-multi-env-setup
Claude Code
Cursor
Copilot
Codex
Antigravity

Details

Pathplugins/saas-packs/clerk-pack/skills/clerk-multi-env-setup/SKILL.md
Branchmain
Scoped Name@jeremylongshore/clerk-multi-env-setup

Usage

After installing, this skill will be available to your AI coding assistant.

Verify installation:

skills list

Skill Instructions


name: clerk-multi-env-setup description: | Configure Clerk for multiple environments (dev, staging, production). Use when setting up environment-specific configurations, managing multiple Clerk instances, or implementing environment promotion. Trigger with phrases like "clerk environments", "clerk staging", "clerk dev prod", "clerk multi-environment". allowed-tools: Read, Write, Edit, Bash(npm:*), Grep version: 1.0.0 license: MIT author: Jeremy Longshore jeremy@intentsolutions.io

Clerk Multi-Environment Setup

Overview

Configure Clerk across development, staging, and production environments.

Prerequisites

  • Clerk account with multiple instances
  • Understanding of environment management
  • CI/CD pipeline configured

Instructions

Step 1: Create Clerk Instances

Create separate Clerk instances for each environment in the Clerk Dashboard:

  • myapp-dev - Development
  • myapp-staging - Staging
  • myapp-prod - Production

Step 2: Environment Configuration

# .env.development.local
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_dev_...
CLERK_SECRET_KEY=sk_test_dev_...
NEXT_PUBLIC_APP_ENV=development

# .env.staging.local
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_staging_...
CLERK_SECRET_KEY=sk_test_staging_...
NEXT_PUBLIC_APP_ENV=staging

# .env.production.local
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_...
CLERK_SECRET_KEY=sk_live_...
NEXT_PUBLIC_APP_ENV=production

Step 3: Environment-Aware Configuration

// lib/clerk-config.ts
type Environment = 'development' | 'staging' | 'production'

interface ClerkConfig {
  signInUrl: string
  signUpUrl: string
  afterSignInUrl: string
  afterSignUpUrl: string
  debug: boolean
}

const configs: Record<Environment, ClerkConfig> = {
  development: {
    signInUrl: '/sign-in',
    signUpUrl: '/sign-up',
    afterSignInUrl: '/dashboard',
    afterSignUpUrl: '/onboarding',
    debug: true
  },
  staging: {
    signInUrl: '/sign-in',
    signUpUrl: '/sign-up',
    afterSignInUrl: '/dashboard',
    afterSignUpUrl: '/onboarding',
    debug: true
  },
  production: {
    signInUrl: '/sign-in',
    signUpUrl: '/sign-up',
    afterSignInUrl: '/dashboard',
    afterSignUpUrl: '/onboarding',
    debug: false
  }
}

export function getClerkConfig(): ClerkConfig {
  const env = (process.env.NEXT_PUBLIC_APP_ENV as Environment) || 'development'
  return configs[env]
}

// Validate environment at startup
export function validateClerkEnvironment() {
  const pk = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
  const env = process.env.NEXT_PUBLIC_APP_ENV

  if (env === 'production' && pk?.startsWith('pk_test_')) {
    throw new Error('Production environment using test keys!')
  }

  if (env !== 'production' && pk?.startsWith('pk_live_')) {
    console.warn('Non-production environment using live keys')
  }
}

Step 4: ClerkProvider Configuration

// app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs'
import { getClerkConfig, validateClerkEnvironment } from '@/lib/clerk-config'

// Validate on startup
validateClerkEnvironment()

export default function RootLayout({ children }) {
  const config = getClerkConfig()

  return (
    <ClerkProvider
      signInUrl={config.signInUrl}
      signUpUrl={config.signUpUrl}
      afterSignInUrl={config.afterSignInUrl}
      afterSignUpUrl={config.afterSignUpUrl}
    >
      <html>
        <body>
          {config.debug && <EnvironmentBanner />}
          {children}
        </body>
      </html>
    </ClerkProvider>
  )
}

function EnvironmentBanner() {
  const env = process.env.NEXT_PUBLIC_APP_ENV

  if (env === 'production') return null

  const colors = {
    development: 'bg-green-500',
    staging: 'bg-yellow-500'
  }

  return (
    <div className={`${colors[env]} text-white text-center text-sm py-1`}>
      {env?.toUpperCase()} ENVIRONMENT
    </div>
  )
}

Step 5: Webhook Configuration Per Environment

// app/api/webhooks/clerk/route.ts
import { headers } from 'next/headers'

const WEBHOOK_SECRETS = {
  development: process.env.CLERK_WEBHOOK_SECRET_DEV,
  staging: process.env.CLERK_WEBHOOK_SECRET_STAGING,
  production: process.env.CLERK_WEBHOOK_SECRET
}

export async function POST(req: Request) {
  const env = process.env.NEXT_PUBLIC_APP_ENV as keyof typeof WEBHOOK_SECRETS
  const WEBHOOK_SECRET = WEBHOOK_SECRETS[env]

  if (!WEBHOOK_SECRET) {
    console.error(`No webhook secret for environment: ${env}`)
    return Response.json({ error: 'Configuration error' }, { status: 500 })
  }

  // ... rest of webhook handling
}

Step 6: CI/CD Environment Promotion

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main, staging]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set environment
        run: |
          if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
            echo "DEPLOY_ENV=production" >> $GITHUB_ENV
            echo "CLERK_PUBLISHABLE_KEY=${{ secrets.CLERK_PUBLISHABLE_KEY_PROD }}" >> $GITHUB_ENV
            echo "CLERK_SECRET_KEY=${{ secrets.CLERK_SECRET_KEY_PROD }}" >> $GITHUB_ENV
          else
            echo "DEPLOY_ENV=staging" >> $GITHUB_ENV
            echo "CLERK_PUBLISHABLE_KEY=${{ secrets.CLERK_PUBLISHABLE_KEY_STAGING }}" >> $GITHUB_ENV
            echo "CLERK_SECRET_KEY=${{ secrets.CLERK_SECRET_KEY_STAGING }}" >> $GITHUB_ENV
          fi

      - name: Build
        run: npm run build
        env:
          NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: ${{ env.CLERK_PUBLISHABLE_KEY }}
          NEXT_PUBLIC_APP_ENV: ${{ env.DEPLOY_ENV }}

      - name: Deploy to Vercel
        run: vercel deploy --prod
        env:
          VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}

Step 7: User Data Isolation

// lib/user-sync.ts
// Ensure user data doesn't leak between environments

export async function syncUser(clerkUser: any) {
  const env = process.env.NEXT_PUBLIC_APP_ENV

  await db.user.upsert({
    where: {
      clerkId_environment: {
        clerkId: clerkUser.id,
        environment: env
      }
    },
    update: { /* ... */ },
    create: {
      clerkId: clerkUser.id,
      environment: env,
      // ... other fields
    }
  })
}

Environment Matrix

EnvironmentKeysDomainData
Developmentpk_test_devlocalhost:3000Dev DB
Stagingpk_test_stagingstaging.myapp.comStaging DB
Productionpk_livemyapp.comProd DB

Output

  • Separate Clerk instances per environment
  • Environment-aware configuration
  • Webhook handling per environment
  • CI/CD pipeline configured

Best Practices

  1. Never share keys between environments
  2. Use test keys for non-production
  3. Validate key/environment match at startup
  4. Separate webhook secrets per environment
  5. Isolate user data by environment

Error Handling

ErrorCauseSolution
Wrong environment keysMisconfigurationValidate at startup
Webhook signature failsWrong secretCheck env-specific secret
User not foundEnv mismatchCheck environment isolation

Resources

Next Steps

Proceed to clerk-observability for monitoring and logging.

More by jeremylongshore

View all
rabbitmq-queue-setup
1,004

Rabbitmq Queue Setup - Auto-activating skill for Backend Development. Triggers on: rabbitmq queue setup, rabbitmq queue setup Part of the Backend Development skill category.

model-evaluation-suite
1,004

evaluating-machine-learning-models: This skill allows Claude to evaluate machine learning models using a comprehensive suite of metrics. It should be used when the user requests model performance analysis, validation, or testing. Claude can use this skill to assess model accuracy, precision, recall, F1-score, and other relevant metrics. Trigger this skill when the user mentions "evaluate model", "model performance", "testing metrics", "validation results", or requests a comprehensive "model evaluation".

neural-network-builder
1,004

building-neural-networks: This skill allows Claude to construct and configure neural network architectures using the neural-network-builder plugin. It should be used when the user requests the creation of a new neural network, modification of an existing one, or assistance with defining the layers, parameters, and training process. The skill is triggered by requests involving terms like "build a neural network," "define network architecture," "configure layers," or specific mentions of neural network types (e.g., "CNN," "RNN," "transformer").

oauth-callback-handler
1,004

Oauth Callback Handler - Auto-activating skill for API Integration. Triggers on: oauth callback handler, oauth callback handler Part of the API Integration skill category.