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:
@sentry/node (or framework-specific SDK like @sentry/nextjs, @sentry/react) installedsentry-cli installed globally or as a devDependency (npm i -D @sentry/cli)SENTRY_AUTH_TOKEN with scope project:releases available in CI environmentWork through each section in order. Check off each item as you verify it.
// CORRECT — DSN from environment
Sentry.init({
dsn: process.env.SENTRY_DSN,
});
// WRONG — hardcoded DSN leaks project ID and org info
Sentry.init({
dsn: 'https://abc123@o456.ingest.sentry.io/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 operationsenvironment tag set to 'production'Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV || 'production',
});
This enables environment-scoped alert rules and release health filtering. Without it, all events land in the default (empty) environment.
release set to match the deploy versionSentry.init({
dsn: process.env.SENTRY_DSN,
environment: 'production',
release: process.env.SENTRY_RELEASE || `myapp@${process.env.npm_package_version}`,
});
The release value must match exactly what you pass to sentry-cli releases new. Mismatches break source map resolution and release health tracking.
sampleRate tuned — not 1.0 in high-traffic productionSentry.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 |
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.
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.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
beforeSend Noise FilterbeforeSend configured to drop noiseSentry.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;
},
});
sendDefaultPii: falseSentry.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:
ssn, creditCard)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 |
Sentry.flush() called before process exitFor serverless functions, CLI tools, or any short-lived process, events may be lost if the process exits before the SDK flushes its queue:
// AWS Lambda / serverless
import * as Sentry from '@sentry/node';
export const handler = Sentry.wrapHandler(async (event) => {
// Your handler logic
return { statusCode: 200 };
});
// CLI tool / script
async function main() {
try {
await doWork();
} catch (err) {
Sentry.captureException(err);
await Sentry.flush(2000); // 2000ms timeout — enough for network round-trip
process.exit(1);
}
}
For long-running servers (Express, Fastify), this is handled automatically — skip this step.
// Send a test error after deploy
Sentry.captureException(new Error('Production deploy verification — safe to ignore'));
// Or from CLI
// node -e "
// const Sentry = require('@sentry/node');
// Sentry.init({ dsn: process.env.SENTRY_DSN, environment: 'production' });
// Sentry.captureMessage('Deploy verification test');
// Sentry.flush(2000).then(() => console.log('Test event sent')); // 2s flush timeout
// "
After sending, verify in the Sentry dashboard:
environment:productionIn Sentry dashboard under Settings > Inbound Filters, enable:
These filters drop events server-side before they count against your quota, unlike beforeSend which drops client-side after the network request.
Run this after completing all checklist items:
#!/bin/bash
# scripts/verify-sentry-prod.sh
set -euo pipefail
echo "=== Sentry Production Verification ==="
PASS=0; FAIL=0
check() {
if eval "$2" > /dev/null 2>&1; then
echo " PASS: $1"; ((PASS++))
else
echo " FAIL: $1"; ((FAIL++))
fi
}
# Environment variables
for var in SENTRY_DSN SENTRY_ORG SENTRY_PROJECT SENTRY_AUTH_TOKEN; do
check "$var is set" "[ -n \"\${$var:-}\" ]"
done
# CLI authentication
check "sentry-cli authenticated" "sentry-cli info"
# Source maps uploaded
RELEASE="${SENTRY_RELEASE:-$(git rev-parse --short HEAD)}"
check "Source maps for $RELEASE" \
"[ \$(sentry-cli releases files \"$RELEASE\" list 2>/dev/null | wc -l) -gt 1 ]"
# Network connectivity
check "sentry.io reachable" \
"[ \$(curl -s -o /dev/null -w '%{http_code}' https://sentry.io/api/0/) = '200' ]"
# No hardcoded DSN in source
check "No hardcoded DSN in src/" \
"! grep -r 'ingest.sentry.io' src/ --include='*.ts' --include='*.js' --include='*.tsx' 2>/dev/null | grep -v node_modules"
echo ""
echo "=== Results: $PASS passed, $FAIL failed ==="
[ "$FAIL" -eq 0 ] && echo "All checks passed." || exit 1
After completing this checklist, you will have:
Sentry.init() with tuned sample rates and PII scrubbingsentry-cli for the current releasebeforeSend filtering known browser noise before it burns quota| Error | Cause | Solution |
|-------|-------|----------|
| Stack traces show minified code | Source maps missing or --url-prefix wrong | Re-upload with correct prefix; run sentry-cli sourcemaps explain to diagnose |
| Events not appearing in dashboard | Wrong DSN or network blocked | Verify SENTRY_DSN is the production DSN; check firewall allows *.ingest.sentry.io on HTTPS |
| release mismatch warning | SDK release differs from CLI release | Ensure Sentry.init({ release }) matches sentry-cli releases new $VERSION exactly |
| Events from wrong environment | environment not set in SDK init | Explicitly set environment: 'production' — do not rely on defaults |
| Excessive event volume / quota exhausted | sampleRate: 1.0 on high-traffic service | Lower to 0.1-0.25; add beforeSend filters; enable server-side inbound filters |
| Alerts not firing | Alert rules scoped to wrong environment | Edit alert: filter to environment:production; test with Sentry.captureMessage() |
| Events lost in serverless / CLI | Process exits before flush | Call await Sentry.flush(2000) before process.exit() or use Sentry.wrapHandler() |
| beforeSend dropping real errors | Overly broad noise filter | Audit filter patterns; log dropped events to a secondary sink during rollout |
Example 1: Full production init (Node.js / Express)
import * as Sentry from '@sentry/node';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: 'production',
release: process.env.SENTRY_RELEASE,
debug: false,
sendDefaultPii: false,
sampleRate: 0.25,
tracesSampleRate: 0.1,
maxBreadcrumbs: 50,
attachStacktrace: true,
ignoreErrors: [
'ResizeObserver loop',
'Non-Error promise rejection captured',
/Loading chunk \d+ failed/,
],
beforeSend(event, hint) {
if (event.request?.headers) {
delete event.request.headers['Authorization'];
delete event.request.headers['Cookie'];
}
return event;
},
tracesSampler: (ctx) => {
const name = ctx.transactionContext?.name || '';
if (name.includes('/health')) return 0;
if (name.includes('/payment')) return 1.0;
return 0.1;
},
});
Example 2: CI source map upload (GitHub Actions)
# .github/workflows/deploy.yml
- name: Upload source maps to Sentry
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: my-org
SENTRY_PROJECT: my-project
VERSION: ${{ github.sha }}
run: |
npx sentry-cli releases new "$VERSION"
npx sentry-cli releases set-commits "$VERSION" --auto
npx sentry-cli releases files "$VERSION" upload-sourcemaps ./dist \
--url-prefix '~/static/js' --validate
npx sentry-cli releases finalize "$VERSION"
npx sentry-cli releases deploys "$VERSION" new -e production
Example 3: Serverless flush pattern (AWS Lambda)
import * as Sentry from '@sentry/node';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: 'production',
release: process.env.SENTRY_RELEASE,
tracesSampleRate: 0.2,
});
export const handler = Sentry.wrapHandler(async (event, context) => {
// wrapHandler automatically calls flush() on completion
const result = await processEvent(event);
return { statusCode: 200, body: JSON.stringify(result) };
});
new, set-commits, finalize, deploysAfter completing this production checklist:
@sentry/browser) — capture user sessions for faster debugging下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer