From Localhost to Production: The Ultimate Deployment Checklist
Your app works perfectly on localhost. Now what? This comprehensive deployment checklist covers everything from environment variables and CI/CD pipelines to monitoring, security hardening, and post-launch operations.
Introduction: The Gap Between "It Works" and "It's Ready"
Every developer knows the feeling. You have built something genuinely useful. It runs beautifully on localhost. The features work, the tests pass, the UI looks polished. You are ready to share it with the world. And then you realize: deploying to production is an entirely different discipline from building the application itself.
The gap between localhost and production is where careers are made and weekends are lost. A missing environment variable can take down your entire service. An unoptimized database query that was fine with 100 rows will bring your server to its knees with 100,000. A forgotten CORS header will make your frontend silently fail in production while working perfectly in development.
This checklist is the result of years of deploying web applications, making every possible mistake, and slowly building a system to prevent those mistakes from happening again. It covers everything from the pre-deployment essentials to post-launch monitoring. Print it, bookmark it, or better yet — automate it into your CI/CD pipeline.
Phase 1: Pre-Deployment Foundation
Environment Variables and Configuration
Environment variables are the single most common source of production bugs. They are also the easiest to prevent.
- Audit every environment variable — List every
process.env.VARIABLEin your codebase. Verify that each one has a corresponding value in your production environment. Missing variables should crash on startup, not fail silently at runtime. - Never hardcode secrets — API keys, database passwords, JWT secrets — none of these belong in your code. Use a secrets manager (AWS Secrets Manager, HashiCorp Vault, Doppler) for production. For development, use
.env.localfiles that are gitignored. - Validate on startup — Use a library like
envalidorzodto validate environment variables when your application starts. Define the expected types, formats, and required variables. If validation fails, the app should refuse to start with a clear error message. - Document every variable — Maintain an
.env.examplefile with every variable, its purpose, and an example value (not the actual value). This is your onboarding document for new developers.
// startup-validation.ts
import { z } from "zod";
const envSchema = z.object({
NODE_ENV: z.enum(["development", "staging", "production"]),
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
REDIS_URL: z.string().url().optional(),
SMTP_HOST: z.string(),
SMTP_PORT: z.coerce.number(),
});
export const env = envSchema.parse(process.env);
Database Readiness
- Run migrations — Ensure all database migrations are applied in the correct order. Use a migration tool (Prisma Migrate, Knex migrations, Flyway) and test the full migration chain from an empty database.
- Seed essential data — Admin accounts, default configurations, lookup tables — anything the application needs to function should be seeded as part of the deployment process.
- Connection pooling — Configure connection pooling with appropriate limits. Start with
connectionLimit: 10and adjust based on your expected concurrency. Monitor for pool exhaustion. - Backup strategy — Before your first production deployment, have automated backups configured and tested. Can you restore from a backup? Have you actually tried it?
- Indexes — Review every query your application executes. Add indexes for columns used in WHERE, JOIN, and ORDER BY clauses. Use
EXPLAINto verify that your queries use indexes effectively.
Security Hardening
- HTTPS everywhere — No exceptions. Use Let's Encrypt for free certificates. Configure your web server to redirect all HTTP traffic to HTTPS. Set
Strict-Transport-Securityheaders. - CORS configuration — Whitelist only the specific origins that need to access your API. Never use
Access-Control-Allow-Origin: *in production for authenticated endpoints. - Rate limiting — Implement rate limiting on all public endpoints. Start with 100 requests per minute per IP and adjust based on your use case. Use Redis-backed rate limiting for distributed deployments.
- Input validation — Validate all user input on the server side. Client-side validation is for UX, server-side validation is for security. Use schema validation libraries like Zod or Joi.
- Authentication and authorization — Verify that authentication middleware is applied to every protected route. Test that unauthenticated requests are properly rejected. Test that users cannot access resources belonging to other users.
- Dependency audit — Run
npm auditand fix all high and critical vulnerabilities. Set up automated dependency scanning (Dependabot, Snyk) for ongoing monitoring. - Security headers — Configure Content-Security-Policy, X-Content-Type-Options, X-Frame-Options, and Referrer-Policy headers. Use a tool like Helmet.js for Express applications.
Phase 2: Build and CI/CD Pipeline
Build Process
- Production build succeeds — Run the production build locally before pushing.
npm run buildshould complete without errors or warnings. TypeScript strict mode should be enabled. - Bundle analysis — Check your bundle sizes. For web applications, the initial JavaScript bundle should be under 200KB gzipped. Use tools like
next-bundle-analyzerorwebpack-bundle-analyzerto identify large dependencies. - Dead code elimination — Ensure tree-shaking is working. Import only what you need from libraries. Use tools like
knipto find unused exports and dependencies. - Asset optimization — Images should be compressed and served in modern formats (WebP, AVIF). Use responsive images with
srcset. Enable asset hashing for cache busting.
CI/CD Pipeline Configuration
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run type-check
- run: npm run test
- run: npm run build
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to production
run: |
# Your deployment commands here
- Automated testing — Every push should run your full test suite. If tests fail, the deployment should be blocked. No exceptions.
- Linting and type checking — Run ESLint and TypeScript type checking as separate CI steps. They catch different categories of errors and both should pass before deployment.
- Branch protection — Configure branch protection rules on your main branch. Require passing CI checks and at least one code review before merging.
- Staging environment — Deploy to a staging environment before production. Test critical flows in staging. Use production-like data (anonymized) for realistic testing.
Phase 3: Infrastructure and Hosting
Containerization
- Docker multi-stage builds — Use multi-stage Dockerfiles to keep production images small. Your final image should contain only the production dependencies and built assets, not the dev dependencies or source files.
- Non-root user — Run your application as a non-root user inside the container. This limits the damage if the container is compromised.
- Health checks — Define a health check endpoint (
/healthor/api/health) that returns 200 when the application is ready to serve traffic. Configure Docker and your orchestrator to use this endpoint. - Resource limits — Set CPU and memory limits on your containers. Without limits, a memory leak in one container can take down the entire host.
DNS and Domain Configuration
- DNS propagation — Configure DNS records at least 24 hours before launch. A records for root domains, CNAME records for subdomains. Verify propagation with
digor online tools. - SSL certificate — Verify that your SSL certificate covers all subdomains you will use. Test with SSL Labs (ssllabs.com/ssltest) and aim for an A+ rating.
- WWW redirect — Decide whether your canonical URL is
www.example.comorexample.comand redirect the other. Be consistent.
Phase 4: Monitoring and Observability
Error Tracking
- Set up error monitoring — Use Sentry, Bugsnag, or a similar service to catch and report runtime errors. Configure source maps so error stack traces reference your original source code, not minified bundles.
- Alert thresholds — Configure alerts for error rate spikes. A sudden increase in 500 errors should wake someone up, not sit in a dashboard until Monday.
- Error grouping — Configure error grouping rules so the same error does not generate 10,000 separate alerts. Group by error type and stack trace.
Performance Monitoring
- Response time tracking — Monitor p50, p95, and p99 response times for your API endpoints. A healthy API should have p95 under 500ms for most endpoints.
- Database query monitoring — Log slow queries (over 1 second) and review them weekly. A single unoptimized query can be the bottleneck for your entire application.
- Uptime monitoring — Use an external uptime monitor (UptimeRobot, Pingdom, Better Uptime) that checks your site every minute from multiple geographic locations.
Logging
- Structured logging — Use JSON-formatted logs with consistent fields (timestamp, level, message, request ID, user ID). Unstructured logs are nearly impossible to search at scale.
- Log levels — Use appropriate log levels: ERROR for failures requiring attention, WARN for degraded performance, INFO for significant operations, DEBUG for development details. Production should log at INFO level by default.
- Log retention — Define a log retention policy. 30 days is typical for most applications. Store logs in a centralized service (CloudWatch, Datadog, Grafana Loki) for searchability.
Phase 5: Post-Launch Operations
The First 24 Hours
- Watch error rates — Monitor error rates closely for the first 24 hours. Be ready to roll back if error rates spike above your threshold.
- Check performance — Run Lighthouse audits on your production pages. Target scores of 90+ for Performance, Accessibility, Best Practices, and SEO.
- Test critical flows — Manually test user registration, login, payment, and any other critical user flows in production. Automated tests catch most issues, but manual testing catches the rest.
- Verify emails and notifications — If your application sends emails, verify they are actually arriving and not being caught by spam filters. Test with multiple email providers (Gmail, Outlook, Yahoo).
Ongoing Operations
- Dependency updates — Schedule monthly dependency updates. Review changelogs, update, test, and deploy. Falling behind on updates creates security debt that compounds over time.
- Database maintenance — Schedule regular database maintenance tasks: index optimization, query plan analysis, storage monitoring. Most databases benefit from periodic
ANALYZEandVACUUMoperations. - Disaster recovery testing — Quarterly, test your disaster recovery plan. Can you restore from a backup? Can you deploy from scratch? How long does it take? Document the process and time it.
- Runbooks — Create runbooks for common operational tasks: scaling up, rolling back, handling database migrations, responding to outages. Runbooks should be step-by-step instructions that any team member can follow under pressure.
The Checklist Summary
Here is the condensed version you can print and tape to your monitor:
- All environment variables documented, validated, and set
- Database migrations run and verified
- Database indexes reviewed and optimized
- Backups configured and tested
- HTTPS configured with proper certificates
- CORS, rate limiting, and security headers set
- Input validation on all server endpoints
- Dependency vulnerabilities audited and resolved
- Production build succeeds with no errors
- Bundle sizes within acceptable limits
- CI/CD pipeline with tests, linting, and type checking
- Staging environment tested
- Docker health checks and resource limits
- DNS configured and propagated
- Error monitoring with alerting
- Performance monitoring for response times
- Structured logging with retention policy
- Uptime monitoring from external service
- Critical user flows manually tested in production
- Runbooks written for common operations
Conclusion: Ship With Confidence
Deploying to production does not have to be terrifying. With the right checklist, the right automation, and the right monitoring, you can ship with confidence knowing that you have covered the most common failure modes. You will still encounter production issues — every application does — but you will catch them faster, recover quicker, and learn from each one.
The best deployment process is the one you have practiced so many times that it is boring. Boring deployments mean your users never notice the transition from localhost to production. And that is exactly how it should be.