Execute production deployment checklist for Sentry integration.
Use when preparing for production deployment, reviewing
Sentry configuration, or verifying production readiness.
Trigger with phrases like "sentry production", "deploy sentry",
"sentry checklist", "sentry go-live".
Walk through every production-critical Sentry configuration item before a deploy — SDK init options, source map uploads, alert routing, PII scrubbing, sample rate tuning, and test error verification. Covers @sentry/node (v8+) and sentry-cli workflows.
Use when:
Preparing a first production deploy with Sentry
Auditing an existing Sentry config after an incident
Running a go-live readiness review
Onboarding a new service into Sentry monitoring
Prerequisites
@sentry/node (or framework-specific SDK like @sentry/nextjs, @sentry/react) installed
Sentry project created with a dedicated production DSN (separate from dev/staging)
sentry-cli installed globally or as a devDependency (npm i -D @sentry/cli)
SENTRY_AUTH_TOKEN with scope project:releases available in CI environment
Build pipeline that produces source maps
Instructions
Work through each section in order. Check off each item as you verify it.
Step 1 — DSN and Environment Variables
DSN set via environment variable, not hardcoded in source code
// CORRECT — DSN from environment
Sentry.init({
dsn: process.env.SENTRY_DSN,
});
// WRONG — hardcoded DSN leaks project ID and org info
Sentry.init({
dsn: 'https://[email protected]/789',
});
Verify with: grep -r "ingest.sentry.io" src/ --include="*.ts" --include="*.js" — should return zero results.
SENTRY_DSN set in production environment (not just .env.local)
SENTRY_ORG and SENTRY_PROJECT set for CLI operations
The release value must match exactly what you pass to sentry-cli releases new. Mismatches break source map resolution and release health tracking.
Step 4 — Error Sample Rate
sampleRate tuned — not 1.0 in high-traffic production
Sentry.init({
// For most apps: 0.1 to 0.25 captures enough to spot trends
// without burning through your event quota
sampleRate: 0.25, // 25% of errors captured
// For low-traffic apps or critical services, 1.0 is fine
// sampleRate: 1.0,
});
Traffic level
Recommended sampleRate
< 10K errors/day
1.0
10K-100K errors/day
0.25
> 100K errors/day
0.1
Step 5 — Traces Sample Rate
tracesSampleRate tuned (0.05-0.2 for production)
Sentry.init({
tracesSampleRate: 0.1, // 10% of transactions traced
// Or use tracesSampler for endpoint-specific rates
tracesSampler: (samplingContext) => {
const name = samplingContext.transactionContext?.name || '';
// Never trace health checks
if (name.includes('/health') || name.includes('/ready')) return 0;
// Always trace payments
if (name.includes('/checkout') || name.includes('/payment')) return 1.0;
// Default: 10%
return 0.1;
},
});
Never ship tracesSampleRate: 1.0 in production — it generates massive event volume and will exhaust your quota within hours on any real workload.
Step 6 — Source Map Upload
Source maps uploaded via sentry-cli releases files
#!/bin/bash
# Run in CI after build, before deploy
set -euo pipefail
VERSION="${SENTRY_RELEASE:-$(git rev-parse --short HEAD)}"
# Create the release
sentry-cli releases new "$VERSION"
# Associate commits for suspect commits feature
sentry-cli releases set-commits "$VERSION" --auto
# Upload source maps from build output
sentry-cli releases files "$VERSION" upload-sourcemaps ./dist \
--url-prefix '~/static/js' \
--validate
# Mark release as deployed
sentry-cli releases finalize "$VERSION"
sentry-cli releases deploys "$VERSION" new -e production
--url-prefix matches the path where assets are served (e.g., ~/static/js or ~/assets)
--validate flag is used to catch malformed maps at build time
Source maps are NOT served to browsers (add to .gitignore or strip from deploy artifact)
Verify upload succeeded:
sentry-cli releases files "$VERSION" list
# Should show .js and .js.map files with correct paths
Step 7 — beforeSend Noise Filter
beforeSend configured to drop noise
Sentry.init({
beforeSend(event, hint) {
const error = hint?.originalException;
const message = typeof error === 'string' ? error : error?.message || '';
// Drop browser noise that is never actionable
const noise = [
'ResizeObserver loop',
'Non-Error promise rejection captured',
/Loading chunk \d+ failed/,
'Network request failed',
'AbortError',
'TypeError: cancelled',
'TypeError: Failed to fetch',
];
for (const pattern of noise) {
if (pattern instanceof RegExp ? pattern.test(message) : message.includes(pattern)) {
return null; // Drop this event
}
}
// Scrub auth headers if they leak into request context
if (event.request?.headers) {
delete event.request.headers['Authorization'];
delete event.request.headers['Cookie'];
delete event.request.headers['X-API-Key'];
}
return event;
},
});
Step 8 — PII Scrubbing
PII scrubbing enabled: sendDefaultPii: false
Sentry.init({
sendDefaultPii: false, // Do NOT send cookies, user IPs, or auth headers
// If you need user context, set it explicitly with scrubbed data
// Sentry.setUser({ id: user.id }); // ID only, no email/name
});
Also verify in Sentry project settings:
Settings > Security & Privacy > Data Scrubbing is enabled
IP Address collection is disabled if not needed
Sensitive Fields list includes your app-specific fields (e.g., ssn, creditCard)
Step 9 — Alert Rules
Alert rules configured for production environment
Set up these minimum alert rules in Alerts > Create Alert Rule:
Alert
Type
Condition
Action
New issue in production
Issue
First seen, environment:production
Slack channel + email
Regression detected
Issue
Regressed, environment:production
Slack channel
Error spike
Metric
Error count > N in 5 min
PagerDuty (on-call)
P95 latency breach
Metric
p95(transaction.duration) > threshold
Slack #performance
Crash-free rate drop
Release health
Crash-free sessions < 99%
Email + Slack
Alert actions route to correct channels (not a dead channel)
PagerDuty / OpsGenie integration tested with a test alert
Step 10 — Process Exit Flush (Serverless / CLI)
Sentry.flush() called before process exit
For serverless functions, CLI tools, or any short-lived process, events may be lost if the process exits before the SDK flushes its queue: