deploy-production
Access: /template deploy-production or /t deploy-production
Guides you through deploying your MakerKit application to production, including environment setup, database migrations, and monitoring configuration.
What It Does
The deploy-production command helps you:
- Configure production environment variables
- Set up production database
- Configure CDN and caching
- Set up monitoring and error tracking
- Configure custom domains
- Implement security headers
- Set up CI/CD pipelines
Usage
/template deploy-production "Description"
# or
/t deploy-production "Description"When prompted, specify:
- Deployment platform (Vercel, AWS, etc.)
- Environment configuration
- Domain settings
- Monitoring preferences
Prerequisites
- A completed MakerKit application
- Deployment platform account
- Domain name (optional)
- Commercial MakerKit license from MakerKit
What Gets Created
1. Environment Configuration
Production environment setup:
# .env.production
# Application
NEXT_PUBLIC_APP_URL=https://your-app.com
NEXT_PUBLIC_APP_NAME="Your App"
NODE_ENV=production
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-key
# Stripe
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Email
RESEND_API_KEY=re_...
# Monitoring
NEXT_PUBLIC_SENTRY_DSN=https://...@sentry.io/...
SENTRY_AUTH_TOKEN=...
# Analytics
NEXT_PUBLIC_GA_ID=G-...
NEXT_PUBLIC_MIXPANEL_TOKEN=...2. Vercel Deployment
Vercel configuration:
// vercel.json
{
"framework": "nextjs",
"buildCommand": "pnpm build",
"devCommand": "pnpm dev",
"installCommand": "pnpm install",
"regions": ["iad1"],
"functions": {
"app/api/stripe/webhook/route.ts": {
"maxDuration": 60
}
},
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-DNS-Prefetch-Control",
"value": "on"
},
{
"key": "X-Frame-Options",
"value": "SAMEORIGIN"
},
{
"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": "Permissions-Policy",
"value": "camera=(), microphone=(), geolocation=()"
}
]
},
{
"source": "/_next/static/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
],
"redirects": [
{
"source": "/home",
"destination": "/dashboard",
"permanent": true
}
]
}3. Database Migration Script
Production migration setup:
// scripts/migrate-production.ts
import { createClient } from '@supabase/supabase-js';
import { config } from 'dotenv';
import { execSync } from 'child_process';
// Load production env
config({ path: '.env.production.local' });
async function migrateProduction() {
console.log('🚀 Starting production migration...');
// Backup current database
console.log('📦 Creating database backup...');
execSync(`pg_dump ${process.env.DATABASE_URL} > backup-${Date.now()}.sql`);
// Run migrations
console.log('🔄 Running migrations...');
execSync('pnpm supabase db push --db-url=$DATABASE_URL');
// Verify migrations
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);
const { data, error } = await supabase
.from('schema_migrations')
.select('version')
.order('version', { ascending: false })
.limit(1)
.single();
if (error) {
console.error('❌ Migration verification failed:', error);
process.exit(1);
}
console.log('✅ Migration completed. Latest version:', data.version);
// Run post-migration tasks
await runPostMigrationTasks();
}
async function runPostMigrationTasks() {
// Update search indexes
console.log('🔍 Updating search indexes...');
await supabase.rpc('reindex_search_tables');
// Refresh materialized views
console.log('🔄 Refreshing materialized views...');
await supabase.rpc('refresh_all_materialized_views');
console.log('✅ Post-migration tasks completed');
}4. GitHub Actions CI/CD
Automated deployment pipeline:
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
workflow_dispatch:
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm typecheck
- run: pnpm test
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
with:
version: 8
- run: pnpm install --frozen-lockfile
- name: Pull Vercel Environment
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
- name: Build Project
run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
- name: Deploy to Vercel
id: deploy
run: |
DEPLOYMENT_URL=$(vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }})
echo "deployment-url=$DEPLOYMENT_URL" >> $GITHUB_OUTPUT
- name: Run E2E Tests
run: |
DEPLOYMENT_URL=${{ steps.deploy.outputs.deployment-url }} pnpm test:e2e:production
- name: Notify Deployment
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "Production deployment completed",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "✅ Production deployment successful\n${{ steps.deploy.outputs.deployment-url }}"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}5. Security Headers
Production security configuration:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const response = NextResponse.next();
// Security headers
response.headers.set(
'Content-Security-Policy',
[
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"font-src 'self' data:",
"connect-src 'self' https://api.stripe.com https://*.supabase.co wss://*.supabase.co",
"frame-src 'self' https://checkout.stripe.com",
"object-src 'none'",
"base-uri 'self'",
"form-action 'self'",
"frame-ancestors 'none'",
].join('; ')
);
response.headers.set(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains'
);
return response;
}
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
};6. Production Monitoring
Set up monitoring dashboards:
// lib/monitoring/production.ts
import * as Sentry from '@sentry/nextjs';
import { CLS, FID, LCP, TTFB, getFCP } from 'web-vitals';
// Initialize monitoring
export function initProductionMonitoring() {
// Sentry
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
environment: 'production',
integrations: [
new Sentry.BrowserTracing(),
new Sentry.Replay({
maskAllText: false,
blockAllMedia: false,
}),
],
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});
// Web Vitals
const vitalsUrl = 'https://vitals.vercel-analytics.com/v1/vitals';
function sendToAnalytics(metric: any) {
const body = JSON.stringify({
dsn: process.env.NEXT_PUBLIC_VERCEL_ANALYTICS_ID,
id: metric.id,
page: window.location.pathname,
href: window.location.href,
event_name: metric.name,
value: metric.value.toString(),
speed: navigator.connection?.effectiveType || '',
});
if (navigator.sendBeacon) {
navigator.sendBeacon(vitalsUrl, body);
} else {
fetch(vitalsUrl, {
body,
method: 'POST',
keepalive: true,
});
}
}
// Measure performance
try {
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getTTFB(sendToAnalytics);
} catch (err) {
console.error('Failed to measure web vitals:', err);
}
}
// Custom error boundary
export function ProductionErrorBoundary({
children
}: {
children: React.ReactNode
}) {
return (
<Sentry.ErrorBoundary
fallback={({ error, resetError }) => (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<h1 className="text-2xl font-bold mb-4">
Something went wrong
</h1>
<p className="text-muted-foreground mb-4">
We've been notified and are working on a fix.
</p>
<Button onClick={resetError}>Try again</Button>
</div>
</div>
)}
showDialog
>
{children}
</Sentry.ErrorBoundary>
);
}7. Database Connection Pooling
Optimize database connections:
// lib/database/connection-pool.ts
import { createClient } from '@supabase/supabase-js';
import { Pool } from 'pg';
// Create connection pool for direct queries
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
// Monitor pool health
pool.on('error', (err) => {
console.error('Unexpected error on idle client', err);
Sentry.captureException(err);
});
// Supabase client with custom fetch
export const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!,
{
global: {
fetch: (url, options = {}) => {
return fetch(url, {
...options,
// Add timeout
signal: AbortSignal.timeout(10000),
});
},
},
db: {
schema: 'public',
},
auth: {
persistSession: true,
autoRefreshToken: true,
},
}
);8. CDN Configuration
Set up asset optimization:
// next.config.js
module.exports = {
images: {
domains: ['your-cdn.com'],
loader: 'cloudinary',
path: 'https://res.cloudinary.com/your-cloud/',
formats: ['image/avif', 'image/webp'],
minimumCacheTTL: 31536000,
},
async rewrites() {
return [
{
source: '/assets/:path*',
destination: 'https://your-cdn.com/assets/:path*',
},
];
},
// Asset optimization
compress: true,
poweredByHeader: false,
generateEtags: true,
// Production optimizations
compiler: {
removeConsole: {
exclude: ['error', 'warn'],
},
},
};9. Health Check Endpoint
Production health monitoring:
// app/api/health/route.ts
export async function GET() {
const checks = {
status: 'healthy',
timestamp: new Date().toISOString(),
version: process.env.NEXT_PUBLIC_APP_VERSION || 'unknown',
checks: {} as Record<string, any>,
};
// Check critical services
const healthChecks = [
checkDatabase(),
checkStripe(),
checkEmail(),
checkCache(),
];
const results = await Promise.allSettled(healthChecks);
results.forEach((result, index) => {
const serviceName = ['database', 'stripe', 'email', 'cache'][index];
if (result.status === 'fulfilled') {
checks.checks[serviceName] = result.value;
} else {
checks.checks[serviceName] = {
status: 'unhealthy',
error: result.reason.message,
};
checks.status = 'degraded';
}
});
return NextResponse.json(checks, {
status: checks.status === 'healthy' ? 200 : 503,
headers: {
'Cache-Control': 'no-cache, no-store, must-revalidate',
},
});
}10. Post-Deployment Checklist
Verify production deployment:
// scripts/post-deploy-check.ts
async function verifyDeployment(url: string) {
const checks = [
{ name: 'Homepage loads', check: () => checkUrl(url) },
{ name: 'API health', check: () => checkUrl(`${url}/api/health`) },
{ name: 'Auth works', check: () => checkAuth(url) },
{ name: 'Stripe webhook', check: () => checkWebhook(url) },
{ name: 'SSL certificate', check: () => checkSSL(url) },
{ name: 'Security headers', check: () => checkHeaders(url) },
];
console.log('🔍 Running post-deployment checks...\n');
for (const { name, check } of checks) {
try {
await check();
console.log(`✅ ${name}`);
} catch (error) {
console.error(`❌ ${name}: ${error.message}`);
process.exit(1);
}
}
console.log('\n🎉 All checks passed!');
}Production Checklist
Before going live:
- [ ] Environment variables configured
- [ ] Database migrated and backed up
- [ ] SSL certificate active
- [ ] Security headers configured
- [ ] Error tracking enabled
- [ ] Performance monitoring active
- [ ] Webhooks tested
- [ ] Email sending verified
- [ ] Payment processing tested
- [ ] Backup strategy implemented
- [ ] Incident response plan ready
- [ ] Documentation updated
License Requirement
Important: This command requires a commercial MakerKit license from https://makerkit.dev?atp=MqaGgc MakerKit is a premium SaaS starter kit and requires proper licensing for commercial use.
