Configure Sentry across multiple environments.
Use when setting up Sentry for dev/staging/production,
managing environment-specific configurations, or isolating data.
Trigger with phrases like "sentry environments", "sentry staging setup",
"multi-environment sentry", "sentry dev vs prod".
Configure Sentry to run across development, staging, and production with isolated DSNs, tuned sample rates, environment-aware alert routing, and dashboard filtering. Covers @sentry/node v8+ (TypeScript) and sentry-sdk v2+ (Python), targeting sentry.io or self-hosted Sentry 24.1+. The goal is to capture everything in dev, validate in staging, and protect production with tight sampling and PII scrubbing.
Prerequisites
Sentry organization at sentry.io with at least one project created
DSN strategy decided: single project with environment tags or separate projects per environment (see project-structure-options.md)
.env file management tooling (dotenv, direnv, or platform-native env config)
Instructions
Step 1 — Create Environment-Aware SDK Configuration with Separate DSNs
Each environment gets its own DSN pointing to a dedicated Sentry project. This prevents dev noise from inflating production quotas and allows independent rate limits per environment.
// test/setup.ts — prevent test runs from sending events
const isTestEnv = process.env.NODE_ENV === 'test' || process.env.CI === 'true';
if (!isTestEnv) {
initSentry();
} else {
// Initialize with empty DSN — SDK loads but sends nothing
// All Sentry.captureException() calls become safe no-ops
Sentry.init({ dsn: '' });
}
Step 3 — Wire CI/CD Releases to the Correct Environment
Tag Sentry releases with the deploy target so the Releases dashboard shows which commit is running where. Each sentry-cli releases deploys call records a deployment event on the timeline.
#!/usr/bin/env bash
# scripts/sentry-release.sh — call from CI after deploy
set -euo pipefail
VERSION="${SENTRY_RELEASE:-$(git rev-parse --short HEAD)}"
ENVIRONMENT="${1:?Usage: sentry-release.sh <environment>}"
ORG="${SENTRY_ORG:-my-org}"
PROJECT="${SENTRY_PROJECT:-my-app}"
echo "Creating Sentry release ${VERSION} for ${ENVIRONMENT}..."
# Create release and associate commits
sentry-cli releases --org "$ORG" --project "$PROJECT" new "$VERSION"
sentry-cli releases --org "$ORG" --project "$PROJECT" set-commits "$VERSION" --auto
# Upload source maps (if applicable)
if [ -d "./dist" ]; then
sentry-cli sourcemaps --org "$ORG" --project "$PROJECT" \
upload --release="$VERSION" ./dist
fi
# Finalize and record deployment
sentry-cli releases --org "$ORG" --project "$PROJECT" finalize "$VERSION"
sentry-cli releases --org "$ORG" --project "$PROJECT" \
deploys "$VERSION" new --env "$ENVIRONMENT"
echo "Release ${VERSION} deployed to ${ENVIRONMENT}"
# Usage in CI:
# ./scripts/sentry-release.sh staging # after staging deploy
# ./scripts/sentry-release.sh production # after production deploy
Per-environment .env files with isolated DSNs pointing to separate Sentry projects
Typed SDK init module (config/sentry.ts or config/sentry_config.py) that reads SENTRY_ENVIRONMENT and applies the correct sample rates, PII policy, and filtering
Alert rules routed by environment: PagerDuty for production, Slack for staging, none for dev
Dashboard filter tags (environment, region, service) for scoped queries
CI/CD release script that tags deploys with the correct environment in Sentry's timeline
Test/CI guard that disables Sentry during automated test runs
Error Handling
Error
Cause
Solution
Dev events appearing in production dashboard
Wrong DSN loaded at runtime
Verify SENTRY_DSN points to the correct project; use dotenv with --env-file .env.production
environment: undefined in Sentry events
SENTRY_ENVIRONMENT and NODE_ENV both unset
Set SENTRY_ENVIRONMENT explicitly in deployment config
Staging alerts routing to PagerDuty
Alert rule missing environment filter
Add environment:production condition to PagerDuty alert rules
Quota exhausted by dev/staging events
All environments share one Sentry project
Use separate projects per environment for independent quotas
Source maps not resolving in staging
Release version mismatch
Ensure SENTRY_RELEASE matches between Sentry.init() and sentry-cli upload
beforeSend dropping wanted events
Filter regex too broad
Test filters in staging first; log dropped events with console.warn during rollout
Python sentry_sdk.init() silently fails
DSN env var empty string
Check os.environ.get("SENTRY_DSN") is set; empty string disables the SDK
Examples
TypeScript — Express app with multi-environment Sentry:
// app.ts
import express from 'express';
import * as Sentry from '@sentry/node';
import { initSentry } from './config/sentry';
// Initialize before any other imports that need instrumentation
initSentry();
const app = express();
// Sentry request handler — must be first middleware
Sentry.setupExpressErrorHandler(app);
app.get('/health', (_req, res) => res.json({ status: 'ok' }));
app.get('/api/data', async (_req, res) => {
const span = Sentry.startSpan({ name: 'fetch-data', op: 'db.query' }, () => {
// Your database call here
return { items: [] };
});
res.json(span);
});
// Error handler — Sentry captures unhandled errors automatically
app.use((err: Error, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
Sentry.captureException(err);
res.status(500).json({ error: 'Internal server error' });
});
app.listen(3000, () => {
console.log(`Server running in ${process.env.SENTRY_ENVIRONMENT || process.env.NODE_ENV} mode`);
});
Python — FastAPI with multi-environment Sentry:
# main.py
import os
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
import sentry_sdk
from config.sentry_config import init_sentry
# Initialize before app creation
init_sentry()
app = FastAPI()
@app.get("/health")
async def health():
return {"status": "ok"}
@app.get("/api/data")
async def get_data():
with sentry_sdk.start_span(op="db.query", name="fetch-data"):
# Your database call here
return {"items": []}
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
sentry_sdk.capture_exception(exc)
return JSONResponse(status_code=500, content={"error": "Internal server error"})
if __name__ == "__main__":
import uvicorn
env = os.environ.get("SENTRY_ENVIRONMENT", "development")
print(f"Server running in {env} mode")
uvicorn.run(app, host="0.0.0.0", port=8000)