Deepgram Multi-Environment Setup
Overview
Configure isolated Deepgram environments for development, staging, and production. Each environment uses a separate Deepgram project, scoped API keys, environment-specific model selection, and validated configuration. Includes typed config, client factory, Docker Compose profiles, and Kubernetes overlays.
Environment Strategy
| Setting | Development | Staging | Production |
|---------|------------|---------|------------|
| Model | base (fast, cheap) | nova-3 | nova-3 |
| Concurrency | 5 | 20 | 100 |
| Diarization | Off | On | On |
| PII Redaction | Off | On | On |
| Callback URL | localhost:3000 | staging.example.com | api.example.com |
| Key Rotation | Manual | Monthly | 90-day auto |
Instructions
Step 1: Typed Environment Configuration
interface DeepgramEnvConfig {
apiKey: string;
projectId: string;
model: 'base' | 'nova-2' | 'nova-3';
maxConcurrency: number;
features: {
diarize: boolean;
smart_format: boolean;
redact: string[] | false;
summarize: boolean;
};
callbackBaseUrl?: string;
timeout: number;
}
function loadConfig(env: string): DeepgramEnvConfig {
const configs: Record<string, DeepgramEnvConfig> = {
development: {
apiKey: process.env.DEEPGRAM_API_KEY_DEV!,
projectId: process.env.DEEPGRAM_PROJECT_ID_DEV!,
model: 'base',
maxConcurrency: 5,
features: {
diarize: false,
smart_format: true,
redact: false,
summarize: false,
},
callbackBaseUrl: 'http://localhost:3000',
timeout: 60000,
},
staging: {
apiKey: process.env.DEEPGRAM_API_KEY_STAGING!,
projectId: process.env.DEEPGRAM_PROJECT_ID_STAGING!,
model: 'nova-3',
maxConcurrency: 20,
features: {
diarize: true,
smart_format: true,
redact: ['pci', 'ssn'],
summarize: true,
},
callbackBaseUrl: 'https://staging.example.com',
timeout: 30000,
},
production: {
apiKey: process.env.DEEPGRAM_API_KEY_PRODUCTION!,
projectId: process.env.DEEPGRAM_PROJECT_ID_PRODUCTION!,
model: 'nova-3',
maxConcurrency: 100,
features: {
diarize: true,
smart_format: true,
redact: ['pci', 'ssn'],
summarize: true,
},
callbackBaseUrl: 'https://api.example.com',
timeout: 30000,
},
};
const config = configs[env];
if (!config) throw new Error(`Unknown environment: ${env}. Use: development, staging, production`);
if (!config.apiKey) throw new Error(`DEEPGRAM_API_KEY_${env.toUpperCase()} not set`);
return config;
}
const env = process.env.NODE_ENV ?? 'development';
const config = loadConfig(env);
Step 2: Client Factory
import { createClient, DeepgramClient } from '@deepgram/sdk';
class DeepgramClientFactory {
private static clients = new Map<string, DeepgramClient>();
static getClient(env?: string): DeepgramClient {
const environment = env ?? process.env.NODE_ENV ?? 'development';
if (!this.clients.has(environment)) {
const config = loadConfig(environment);
this.clients.set(environment, createClient(config.apiKey));
console.log(`Deepgram client created for: ${environment} (model: ${config.model})`);
}
return this.clients.get(environment)!;
}
// Convenience: transcribe with environment defaults
static async transcribe(url: string, overrides: Record<string, any> = {}) {
const environment = process.env.NODE_ENV ?? 'development';
const config = loadConfig(environment);
const client = this.getClient(environment);
const { result, error } = await client.listen.prerecorded.transcribeUrl(
{ url },
{
model: config.model,
smart_format: config.features.smart_format,
diarize: config.features.diarize,
redact: config.features.redact || undefined,
summarize: config.features.summarize ? 'v2' : undefined,
...overrides,
}
);
if (error) throw error;
return result;
}
// Reset for key rotation
static reset(env?: string) {
if (env) {
this.clients.delete(env);
} else {
this.clients.clear();
}
}
}
Step 3: Environment Variables Template
# .env.development
DEEPGRAM_API_KEY_DEV=dev-key-here
DEEPGRAM_PROJECT_ID_DEV=dev-project-id
# .env.staging
DEEPGRAM_API_KEY_STAGING=staging-key-here
DEEPGRAM_PROJECT_ID_STAGING=staging-project-id
# .env.production (use secret manager, not file)
# DEEPGRAM_API_KEY_PRODUCTION=production-key-here
# DEEPGRAM_PROJECT_ID_PRODUCTION=production-project-id
Step 4: Docker Compose Multi-Profile
# docker-compose.yml
x-common: &common
build: .
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
services:
app-dev:
<<: *common
profiles: ["development"]
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DEEPGRAM_API_KEY_DEV=${DEEPGRAM_API_KEY_DEV}
- DEEPGRAM_PROJECT_ID_DEV=${DEEPGRAM_PROJECT_ID_DEV}
app-staging:
<<: *common
profiles: ["staging"]
ports:
- "3001:3000"
environment:
- NODE_ENV=staging
- DEEPGRAM_API_KEY_STAGING=${DEEPGRAM_API_KEY_STAGING}
- DEEPGRAM_PROJECT_ID_STAGING=${DEEPGRAM_PROJECT_ID_STAGING}
app-production:
<<: *common
profiles: ["production"]
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DEEPGRAM_API_KEY_PRODUCTION=${DEEPGRAM_API_KEY_PRODUCTION}
- DEEPGRAM_PROJECT_ID_PRODUCTION=${DEEPGRAM_PROJECT_ID_PRODUCTION}
deploy:
resources:
limits:
memory: 512M
# Usage:
docker compose --profile development up
docker compose --profile staging up
docker compose --profile production up
Step 5: Kubernetes Kustomize Overlays
# k8s/base/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: deepgram-config
data:
DEEPGRAM_MODEL: "nova-3"
DEEPGRAM_SMART_FORMAT: "true"
---
# k8s/overlays/development/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
patchesStrategicMerge:
- configmap-patch.yaml
secretGenerator:
- name: deepgram-secrets
literals:
- api-key=dev-key-here
---
# k8s/overlays/development/configmap-patch.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: deepgram-config
data:
DEEPGRAM_MODEL: "base"
DEEPGRAM_MAX_CONCURRENCY: "5"
Step 6: Environment Validation
async function validateEnvironments() {
const envs = ['development', 'staging', 'production'];
const results: Record<string, { valid: boolean; error?: string }> = {};
for (const env of envs) {
try {
const config = loadConfig(env);
const client = createClient(config.apiKey);
// Test 1: Key validity
const { error: authError } = await client.manage.getProjects();
if (authError) throw new Error(`Auth failed: ${authError.message}`);
// Test 2: Project access
const { error: projError } = await client.manage.getProject(config.projectId);
if (projError) throw new Error(`Project access failed: ${projError.message}`);
// Test 3: Transcription works
const { error: sttError } = await client.listen.prerecorded.transcribeUrl(
{ url: 'https://static.deepgram.com/examples/Bueller-Life-moves-702702706.wav' },
{ model: config.model, smart_format: true }
);
if (sttError) throw new Error(`STT failed: ${sttError.message}`);
results[env] = { valid: true };
console.log(`[PASS] ${env}`);
} catch (err: any) {
results[env] = { valid: false, error: err.message };
console.log(`[FAIL] ${env}: ${err.message}`);
}
}
const allValid = Object.values(results).every(r => r.valid);
console.log(`\nValidation: ${allValid ? 'ALL PASS' : 'FAILURES DETECTED'}`);
return results;
}
Output
- Typed environment configuration (dev/staging/prod)
- Singleton client factory per environment
- Docker Compose multi-profile setup
- Kubernetes Kustomize overlays
- Environment validation script
Error Handling
| Issue | Cause | Solution |
|-------|-------|----------|
| DEEPGRAM_API_KEY_DEV not set | Missing env var | Set in .env.development |
| Wrong model in staging | Config mismatch | Check loadConfig mapping |
| Cross-env key used | Shared key | Create separate projects per environment |
| Validation fails for one env | Key expired | Rotate key for that environment |