Before You Deploy a Node.js App: 10 Production Checks Worth Making
A Node.js application can work perfectly on your laptop and still run into problems after deployment. Missing environment variables, vulnerable dependencies, incorrect database settings, and weak error handling are common reasons a release goes wrong.
Before you deploy a Node.js app, take a few minutes to review the production setup. These ten checks cover the essentials, from dependency installation and security to monitoring and recovery. They apply to most Node.js backends, although some examples are specific to Express applications.
1. Keep Production Configuration Out of Your Code
Applications often need database URLs, API keys, authentication secrets, and other environment-specific settings. Keep these values separate from your source code so you can configure each environment without changing the application itself.
Store production secrets in your hosting provider’s secret manager or another suitable secrets management system. Do not commit passwords or API keys to Git, include them in frontend bundles, or print them in logs. If a secret is accidentally committed, remove it from use and rotate it; deleting the file alone may not remove it from Git history.
Before releasing, verify that every required environment variable exists and has the expected format. A missing database URL should be caught during startup rather than causing confusing errors after users begin making requests.
2. Use a Supported Node.js Version
Check which Node.js version your application uses locally, in testing, and on the production server. Using different versions can expose compatibility problems that never appeared during development.
Choose a supported Node.js release suitable for your application, preferably an active or maintenance LTS release, and test upgrades before rolling them out. Pin or explicitly configure the runtime version using your hosting platform’s supported mechanism.
If you use Express, configure NODE_ENV=production where appropriate. Express uses production mode for certain optimizations and less verbose error responses. Test this configuration in staging before deploying it.
The official Node.js guide to development and production explains the distinction.
3. Install Dependencies Reproducibly
A production deployment should install the dependencies recorded by your project’s lockfile instead of silently resolving a different set of package versions.
For projects using npm with a committed package-lock.json, use:
npm ci
This command performs a clean installation and fails if the lockfile conflicts with the dependency manifest. If your project uses pnpm or Yarn, use the corresponding frozen-lockfile installation command instead.
Commit the correct lockfile, and make sure your deployment pipeline does not unexpectedly update it. See the official npm ci documentation for the details.
4. Run Tests and Build the Application
Do not treat a successful deployment command as proof that the application is ready. Run your automated tests and production build before releasing the code.
For an npm project, that might look like this, depending on the scripts defined in your package.json:
npm test
npm run build
Test important user journeys, API validation, authentication, and database interactions. If you use TypeScript, ensure the production build catches type errors. Also test the built application rather than relying only on a development server.
Deploy to a staging environment that resembles production as closely as practical. This helps reveal missing configuration and deployment-specific issues before real users encounter them.
5. Check Dependencies for Known Vulnerabilities
Your application’s security depends partly on the third-party packages it uses. An outdated dependency may contain a known vulnerability, so review dependency security before every important release.
For npm projects, start with:
npm audit
Review the report to understand which packages are affected and whether a fix is available. Update vulnerable dependencies, run the tests again, and verify that the fix has not introduced a compatibility problem.
Avoid blindly running force-update commands just to clear the report. They can introduce breaking changes, and an audit result does not replace a broader security review. The Node.js security best practices provide additional guidance.
6. Protect Routes and Validate User Input
Production APIs should never assume that incoming data is trustworthy. Validate request bodies, query parameters, uploaded files, and identifiers before using them. Apply sensible limits to payload sizes and reject invalid input with appropriate responses.
Protect private routes with proper authentication and authorization. Add rate limiting to sensitive endpoints such as login, password reset, and expensive operations. Use HTTPS for traffic carrying credentials or personal data, and configure cookies with suitable security options when your application uses sessions.
If you use Express, Express’s production security guide covers security headers, TLS, dependency security, and other important measures.
7. Handle Errors Without Exposing Sensitive Details
Errors are inevitable, but the response shown to a user should not reveal database queries, file paths, stack traces, API keys, or internal configuration.
Return an appropriate HTTP status code and a simple error message to the client. Record useful diagnostic details in server-side logs, while making sure those logs do not contain passwords, access tokens, or other sensitive information.
For Express applications, central error-handling middleware can help keep responses consistent. Handle expected failures close to where they occur, and do not continue running as though everything is fine after an unexpected fatal error leaves the process in an unreliable state.
8. Configure Health Checks, Logging, and Restarts
A deployed application needs more than a working homepage. You need a way to tell whether it is running correctly and to investigate problems when something fails.
For an Express app, a simple health endpoint might look like this:
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok' });
});
This basic endpoint only confirms that the application can respond. If your deployment platform needs to know whether the app is ready to serve traffic, define an appropriate readiness check as well, without exposing sensitive diagnostic information publicly.
Use structured logs where practical, collect errors and relevant performance metrics, and configure your process manager or hosting platform to restart the application after a crash. Test that alerts and logs are actually available instead of assuming they are configured correctly.
9. Verify Database Migrations and Backups
Database changes can cause problems even when the application code is correct. Review migrations before deployment and check that they are compatible with the data already stored in production.
Take a backup before a release that could affect existing data, and know how to restore it. A backup that has never been tested is not a reliable recovery plan.
For applications running multiple instances, avoid letting every instance perform the same migration independently at startup. Use a controlled deployment step or the process recommended by your framework and database tooling. Make sure the application can also handle a temporary database outage without leaking sensitive errors.
10. Check Performance and Plan for Rollback
Review operations that could block the Node.js event loop, such as expensive synchronous file operations or CPU-heavy work performed during requests. Set sensible limits for request sizes and timeouts, and check database connection handling.
Run a realistic test if the application will receive significant traffic. A reverse proxy or hosting platform may handle TLS termination, compression, and traffic distribution, but verify which responsibilities belong to the platform and which belong to your application.
Finally, document how to roll back a failed release. Keep the previous working version available where your deployment process supports it, and understand whether a database change can safely be reversed. Deploying is only half the job; recovering safely is part of the plan.
Final Pre-Deployment Checklist
- Production settings are configured and secrets are protected.
- The Node.js runtime and dependencies are supported and reproducible.
- Tests, builds, and important user journeys pass.
- Input validation, authentication, HTTPS, and error handling have been reviewed.
- Logs, health checks, backups, and restart behaviour have been verified.
- A rollback plan exists for the release.
Preparing a Node.js application for production does not require an unnecessarily complicated setup. What matters is knowing how configuration, security, failures, and recovery will be handled once real users depend on the application. Work through these ten checks before every significant release, and you will catch many avoidable deployment problems before they reach production.