Deployment
Production deployment checklist and best practices for your PRIV integration.
Overview
Before deploying your PRIV integration to production, follow this comprehensive checklist to ensure security, performance, and reliability.
Security First
Verify all security configurations are properly set.
Performance Optimized
Ensure minimal impact on your application's performance.
Pre-Deployment Checklist
API Keys
- Switch to production keys: Replace
pk_test_withpk_live_keys - Store keys securely: Use environment variables, not hardcoded values
- Verify key permissions: Ensure keys have minimum required permissions
- Set up key rotation plan: Document how to rotate keys if compromised
# Production environment variables
NEXT_PUBLIC_PRIV_KEY=pk_live_xxxxxxxxxxxxx
PRIV_SECRET_KEY=sk_live_xxxxxxxxxxxxxSecurity Headers
Ensure your application has proper security headers:
// next.config.ts
const securityHeaders = [
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload',
},
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'X-XSS-Protection',
value: '1; mode=block',
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
{
key: 'Content-Security-Policy',
value: `
default-src 'self';
script-src 'self' 'unsafe-inline' https://cdn.priv.io;
connect-src 'self' https://api.priv.io wss://realtime.priv.io;
img-src 'self' data: https:;
style-src 'self' 'unsafe-inline';
`.replace(/\n/g, ''),
},
]
export default {
async headers() {
return [
{
source: '/:path*',
headers: securityHeaders,
},
]
},
}Domain Whitelisting
Register your production domains in the PRIV dashboard:
- Go to Settings then Domains
- Add all production domains
- Add staging/preview domains if needed
Allowed domains:
- example.com
- www.example.com
- app.example.com
- *.vercel.app (for preview deployments)Environment Configuration
Vercel
# Install Vercel CLI
npm i -g vercel
# Set environment variables
vercel env add NEXT_PUBLIC_PRIV_KEY production
vercel env add PRIV_SECRET_KEY production
vercel env add PRIV_WEBHOOK_SECRET production
# Deploy
vercel --prodDocker
# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
# Environment variables should be injected at runtime
# Never bake secrets into the image
EXPOSE 3000
CMD ["node", "server.js"]# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- NEXT_PUBLIC_PRIV_KEY=${NEXT_PUBLIC_PRIV_KEY}
- PRIV_SECRET_KEY=${PRIV_SECRET_KEY}
- PRIV_WEBHOOK_SECRET=${PRIV_WEBHOOK_SECRET}AWS/GCP/Azure
Use your cloud provider's secrets manager:
// AWS Secrets Manager example
import {
SecretsManagerClient,
GetSecretValueCommand,
} from '@aws-sdk/client-secrets-manager'
async function getSecrets() {
const client = new SecretsManagerClient({ region: 'us-east-1' })
const response = await client.send(
new GetSecretValueCommand({
SecretId: 'priv/production',
})
)
return JSON.parse(response.SecretString!)
}SDK Configuration
Production Settings
import { Priv } from '@priv/sdk'
const priv = new Priv({
apiKey: process.env.NEXT_PUBLIC_PRIV_KEY!,
// Production settings
debug: false, // Disable debug logging
batchSize: 20, // Larger batches for efficiency
flushInterval: 10000, // 10 second flush interval
// Privacy
respectDNT: true, // Honor Do Not Track
anonymizeIP: true, // Hash IP addresses
// Error handling
onError: (error) => {
// Send to your error tracking service
Sentry.captureException(error)
},
})Error Tracking Integration
// Integration with Sentry
import * as Sentry from '@sentry/nextjs'
import { Priv } from '@priv/sdk'
const priv = new Priv({
apiKey: process.env.NEXT_PUBLIC_PRIV_KEY!,
onError: (error) => {
Sentry.captureException(error, {
tags: {
sdk: 'priv',
},
extra: {
eventType: error.event?.type,
},
})
},
})Performance Optimization
Bundle Size
The PRIV SDK is designed to be lightweight (under 5KB gzipped). Verify your bundle:
# Check bundle size
npx next build
npx @next/bundle-analyzerLazy Loading
Load the SDK only when needed:
// Lazy load for non-critical pages
const loadPriv = async () => {
const { Priv } = await import('@priv/sdk')
return new Priv({
apiKey: process.env.NEXT_PUBLIC_PRIV_KEY!,
})
}
// Use in component
useEffect(() => {
loadPriv().then(priv => {
priv.trackPageView()
})
}, [])Server-Side Considerations
For SSR applications, ensure SDK only runs on client:
'use client'
import { useEffect } from 'react'
import { Priv } from '@priv/sdk'
export function Analytics() {
useEffect(() => {
// Only run on client
if (typeof window === 'undefined') return
const priv = new Priv({
apiKey: process.env.NEXT_PUBLIC_PRIV_KEY!,
})
priv.trackPageView()
return () => {
priv.flush()
}
}, [])
return null
}Monitoring Setup
Health Checks
Implement health check endpoints:
// app/api/health/route.ts
import { NextResponse } from 'next/server'
export async function GET() {
const checks = {
status: 'healthy',
timestamp: new Date().toISOString(),
services: {
priv: await checkPrivConnection(),
database: await checkDatabaseConnection(),
},
}
const allHealthy = Object.values(checks.services).every(s => s.status === 'healthy')
return NextResponse.json(checks, {
status: allHealthy ? 200 : 503,
})
}
async function checkPrivConnection() {
try {
const response = await fetch('https://api.priv.io/v1/health', {
headers: {
'Authorization': `Bearer ${process.env.PRIV_SECRET_KEY}`,
},
})
return {
status: response.ok ? 'healthy' : 'unhealthy',
latency: response.headers.get('x-response-time'),
}
} catch (error) {
return { status: 'unhealthy', error: String(error) }
}
}Logging
Set up structured logging:
import pino from 'pino'
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
formatters: {
level: (label) => ({ level: label }),
},
})
// Log PRIV events
priv.on('eventSent', (event) => {
logger.debug({ event }, 'PRIV event sent')
})
priv.on('error', (error) => {
logger.error({ error }, 'PRIV SDK error')
})Metrics
Export metrics for monitoring:
import { Counter, Histogram } from 'prom-client'
const eventsSent = new Counter({
name: 'priv_events_sent_total',
help: 'Total number of events sent to PRIV',
labelNames: ['event_type'],
})
const eventLatency = new Histogram({
name: 'priv_event_latency_seconds',
help: 'Event send latency',
buckets: [0.1, 0.5, 1, 2, 5],
})
// Track in SDK callbacks
priv.on('eventSent', (event) => {
eventsSent.inc({ event_type: event.type })
})
priv.on('batchSent', (batch, duration) => {
eventLatency.observe(duration / 1000)
})Webhook Configuration
Production Webhook Setup
-
Create production endpoint:
- Use HTTPS only
- Implement signature verification
- Add rate limiting
-
Register in PRIV dashboard:
- Go to Settings then Webhooks
- Add production URL
- Select required events
- Copy signing secret
-
Set up retry handling:
- Implement idempotency
- Handle duplicate deliveries
- Log all webhook events
// Production webhook handler
import { NextRequest, NextResponse } from 'next/server'
import crypto from 'crypto'
export async function POST(request: NextRequest) {
const body = await request.text()
const signature = request.headers.get('x-priv-signature')
const timestamp = request.headers.get('x-priv-timestamp')
// Verify signature
if (!verifySignature(body, signature, timestamp)) {
logger.warn('Invalid webhook signature')
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 })
}
const event = JSON.parse(body)
// Log for audit trail
logger.info({ eventId: event.id, type: event.type }, 'Webhook received')
try {
// Process with idempotency check
await processWebhookIdempotent(event)
return NextResponse.json({ received: true })
} catch (error) {
logger.error({ error, eventId: event.id }, 'Webhook processing failed')
return NextResponse.json({ error: 'Processing failed' }, { status: 500 })
}
}Final Checklist
Security
- Production API keys configured
- Secret keys not exposed in client code
- Security headers configured
- CSP allows PRIV domains
- Webhook signatures verified
- Rate limiting implemented
Configuration
- Domains whitelisted in dashboard
- Environment variables set correctly
- Debug mode disabled
- Error handling configured
Privacy
- Consent management implemented
- DNT (Do Not Track) respected
- Privacy policy updated
- Data processing agreements in place
Performance
- Bundle size verified
- Lazy loading configured (if needed)
- Batch settings optimized
Monitoring
- Health checks configured
- Error tracking integrated
- Logging set up
- Alerts configured
Testing
- Production smoke tests passed
- Webhook delivery verified
- Events appearing in dashboard
Post-Deployment
Verify Integration
After deployment, verify everything is working:
// Production verification script
async function verifyDeployment() {
console.log('Verifying PRIV integration...')
// 1. Check SDK connection
const priv = new Priv({
apiKey: process.env.NEXT_PUBLIC_PRIV_KEY!,
})
// 2. Send test event
await priv.track('deployment_verification', {
environment: 'production',
timestamp: new Date().toISOString(),
})
await priv.flush()
console.log('Test event sent')
// 3. Verify in dashboard
console.log('Check dashboard for event: deployment_verification')
}Monitor First 24 Hours
- Watch error rates in your monitoring
- Check PRIV dashboard for incoming events
- Verify webhook delivery logs
- Monitor for any rate limiting issues
Rollback Plan
If issues arise, have a rollback plan ready:
// Feature flag to disable PRIV
const priv = new Priv({
apiKey: process.env.NEXT_PUBLIC_PRIV_KEY!,
disabled: process.env.DISABLE_PRIV === 'true',
})
// Or wrap in try-catch
function safeTrack(event: string, properties?: Record<string, unknown>) {
try {
priv.track(event, properties)
} catch (error) {
console.error('PRIV tracking failed:', error)
// Continue without tracking
}
}