plugins/nextjs-master/skills/nextjs-deployment/SKILL.md
Complete Next.js deployment system (Next.js 15.5/16). PROACTIVELY activate for: (1) Vercel deployment and configuration, (2) Self-hosted Node.js deployment, (3) Docker containerization, (4) Static export with output:'export', (5) Edge Runtime configuration, (6) Environment variables, (7) CI/CD with GitHub Actions, (8) Health checks and monitoring, (9) Production optimization, (10) Turbopack production builds (Next.js 16). Provides: Dockerfile, docker-compose, PM2 config, vercel.json, GitHub Actions workflows, Turbopack config. Ensures production-ready deployment with proper configuration.
npx skillsauth add JosiahSiegel/claude-plugin-marketplace nextjs-deploymentInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| Output Mode | Config | Use Case |
|-------------|--------|----------|
| Default | - | Vercel/Node.js hosting |
| Standalone | output: 'standalone' | Docker containers |
| Static | output: 'export' | Static hosting (no SSR) |
| Platform | Command | Notes |
|----------|---------|-------|
| Vercel | vercel --prod | Automatic, recommended |
| Node.js | npm run build && npm start | Self-hosted |
| Docker | docker build -t app . | Container deployment |
| Static | npm run build → out/ | CDN hosting |
| Runtime | Config | Features |
|---------|--------|----------|
| Node.js | Default | Full features |
| Edge | export const runtime = 'edge' | Fast, limited APIs |
| Environment | File | Usage |
|-------------|------|-------|
| Development | .env.local | Local dev |
| Production | .env.production | Build time |
| Runtime | Platform secrets | Server runtime |
Use for deployment and production:
Related skills:
nextjs-middlewarenextjs-cachingnextjs-authentication# Install Vercel CLI
npm i -g vercel
# Deploy
vercel
# Production deployment
vercel --prod
# Set environment variables
vercel env add DATABASE_URL production
# Pull env vars locally
vercel env pull .env.local
{
"buildCommand": "npm run build",
"outputDirectory": ".next",
"framework": "nextjs",
"regions": ["iad1", "sfo1"],
"functions": {
"app/api/**/*.ts": {
"memory": 1024,
"maxDuration": 30
}
},
"headers": [
{
"source": "/api/(.*)",
"headers": [
{ "key": "Access-Control-Allow-Origin", "value": "*" }
]
}
],
"redirects": [
{
"source": "/old-page",
"destination": "/new-page",
"permanent": true
}
],
"rewrites": [
{
"source": "/api/proxy/:path*",
"destination": "https://api.example.com/:path*"
}
]
}
# Build the application
npm run build
# Start production server
npm run start
// server.ts
import { createServer } from 'http';
import { parse } from 'url';
import next from 'next';
const dev = process.env.NODE_ENV !== 'production';
const hostname = process.env.HOSTNAME || 'localhost';
const port = parseInt(process.env.PORT || '3000', 10);
const app = next({ dev, hostname, port });
const handle = app.getRequestHandler();
app.prepare().then(() => {
createServer(async (req, res) => {
try {
const parsedUrl = parse(req.url!, true);
await handle(req, res, parsedUrl);
} catch (err) {
console.error('Error occurred handling', req.url, err);
res.statusCode = 500;
res.end('Internal Server Error');
}
})
.once('error', (err) => {
console.error(err);
process.exit(1);
})
.listen(port, () => {
console.log(`> Ready on http://${hostname}:${port}`);
});
});
// ecosystem.config.js
module.exports = {
apps: [
{
name: 'nextjs-app',
script: 'npm',
args: 'start',
instances: 'max',
exec_mode: 'cluster',
autorestart: true,
watch: false,
max_memory_restart: '1G',
env: {
NODE_ENV: 'production',
PORT: 3000,
},
},
],
};
# Start with PM2
pm2 start ecosystem.config.js
# Monitor
pm2 monit
# Logs
pm2 logs
# Dockerfile
FROM node:20-alpine AS base
# Dependencies stage
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
# Builder stage
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build
# Runner stage
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Automatically leverage output traces
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"
CMD ["node", "server.js"]
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
};
module.exports = nextConfig;
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- '3000:3000'
environment:
- DATABASE_URL=postgresql://user:password@db:5432/mydb
- NEXTAUTH_SECRET=your-secret
- NEXTAUTH_URL=http://localhost:3000
depends_on:
- db
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
// Optional: Change the output directory
distDir: 'dist',
// Optional: Add trailing slashes
trailingSlash: true,
// Optional: Disable image optimization
images: {
unoptimized: true,
},
};
module.exports = nextConfig;
npm run build
# Output in 'out' directory (or distDir if configured)
// app/api/edge/route.ts
export const runtime = 'edge';
export async function GET(request: Request) {
return new Response(JSON.stringify({ message: 'Hello from Edge!' }), {
headers: { 'content-type': 'application/json' },
});
}
// middleware.ts
// Runs on Edge by default
import { NextResponse } from 'next/server';
export function middleware(request: Request) {
return NextResponse.next();
}
import { get } from '@vercel/edge-config';
export const runtime = 'edge';
export async function GET() {
const greeting = await get('greeting');
return Response.json({ greeting });
}
# .env.production
DATABASE_URL=
NEXTAUTH_SECRET=
NEXTAUTH_URL=https://myapp.com
NEXT_PUBLIC_API_URL=https://api.myapp.com
/** @type {import('next').NextConfig} */
const nextConfig = {
// Strict mode for development
reactStrictMode: true,
// Optimize images
images: {
domains: ['images.example.com'],
formats: ['image/avif', 'image/webp'],
},
// Security headers
async headers() {
return [
{
source: '/:path*',
headers: [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
],
},
];
},
// Redirects
async redirects() {
return [
{
source: '/old-blog/:slug',
destination: '/blog/:slug',
permanent: true,
},
];
},
// Experimental features
experimental: {
// Enable PPR
ppr: true,
},
};
module.exports = nextConfig;
// app/api/health/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
try {
// Check database connection
await db.$queryRaw`SELECT 1`;
return NextResponse.json({
status: 'healthy',
timestamp: new Date().toISOString(),
});
} catch (error) {
return NextResponse.json(
{ status: 'unhealthy', error: 'Database connection failed' },
{ status: 503 }
);
}
}
// next.config.js
const { withSentryConfig } = require('@sentry/nextjs');
const nextConfig = {
// Your config
};
module.exports = withSentryConfig(nextConfig, {
silent: true,
org: 'your-org',
project: 'your-project',
});
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 1.0,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});
// app/layout.tsx
import { Analytics } from '@vercel/analytics/react';
import { SpeedInsights } from '@vercel/speed-insights/next';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html>
<body>
{children}
<Analytics />
<SpeedInsights />
</body>
</html>
);
}
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
| Practice | Description | |----------|-------------| | Use output: 'standalone' | Smaller Docker images | | Set proper env vars | Use Vercel/platform secrets | | Enable caching | Use CDN, browser caching | | Monitor performance | Vercel Analytics, Sentry | | Use health checks | Load balancer integration | | Enable compression | Gzip/Brotli in reverse proxy |
development
Use for Clerk sessions, tokens, webhooks, orgs, and security. PROACTIVELY activate for session tokens, JWT templates, getToken(), custom claims, pending sessions, multi-session UX, organizations, roles, permissions, system vs custom permissions, features/plans, MFA/passkeys/password policy/bot protection, Clerk webhooks, Svix signatures, verifyWebhook(), user/org sync, retries/replays, environment variables, custom domains, secret rotation, logs, and auth security reviews. Provides token semantics, webhook idempotency, authorization defaults, and hardening checklist.
tools
Use for Clerk in Next.js. PROACTIVELY activate for @clerk/nextjs setup, App Router auth()/currentUser(), clerkMiddleware(), proxy.ts/middleware.ts, createRouteMatcher(), protected pages/layouts/Route Handlers/Server Actions/API routes/tRPC, auth.protect() role/permission/token checks, ClerkProvider placement, server-only clerkClient, Link prefetch, redirects, 401/404 auth failures, custom domains, __clerk proxy paths, and deployment gotchas. Provides file patterns, server/client boundary rules, matcher templates, and production checks.
development
Use for Clerk frontend auth flows. PROACTIVELY activate for React, JavaScript, Vue, Nuxt, Astro, Expo, React Router, TanStack React Start, or SPA setup; ClerkProvider and publishable-key wiring; SignIn/SignUp/UserButton/UserProfile/OrganizationSwitcher; custom useUser/useAuth/useClerk/useSignIn/useSignUp/useSession/useOrganization flows; multi-session UX; cross-origin getToken() fetches; loading states, redirects, routing, CORS/cookies, or hydration bugs. Provides SDK selection, UI patterns, token-fetch templates, and frontend gotchas.
development
Use for Clerk dev/prod readiness, deployment, and multi-language implementation planning. PROACTIVELY activate for environment variables, pk_test/sk_test vs pk_live/sk_live, local dev, preview/staging/prod instances, domains/DNS, redirects, OAuth credentials, custom domains/proxy, authorizedParties, CSP, CORS/cookies, webhooks/tunnels, Vercel/Netlify/Cloudflare/API gateways, monitoring/troubleshooting, and backends in Node/Express/Fastify, Python/FastAPI/Django/Flask, Go, Ruby/Rails, Java/Spring, .NET, PHP/Laravel. Provides checklists, rollout plans, and language-portable patterns.