Manage deployment configuration in Node.js

Separate non-secret configuration from credentials, validate settings at startup, and choose explicit precedence and secret-delivery rules.

Best practices
Illustration of files, modules, and their dependencies

Keep non-secret defaults in version-controlled configuration and supply environment-specific values through the deployment system. Deliver credentials through a controlled secret mechanism, then validate the effective settings before accepting traffic.

Read environment settings

For local development with dotenv installed:

require('dotenv').config();
const apiKey = process.env.API_KEY;
const databaseUrl = process.env.DATABASE_URL;
if (!apiKey || !databaseUrl) {
  throw new Error('API_KEY and DATABASE_URL are required');
}

Environment variables separate configuration from application code, but are not an encrypted secret store. They can be inherited by children or exposed in diagnostics. Do not log them wholesale. Keep secret-bearing .env files out of version control and commit an .env.example containing only names and safe examples.

Make file selection and merging explicit

A manual configuration loader can choose from a fixed set of environments:

const files = {
  development: './config/development.json',
  production: './config/production.json',
  test: './config/test.json'
};
const environment = process.env.NODE_ENV || 'development';
if (!Object.hasOwn(files, environment)) {
  throw new Error('Unsupported NODE_ENV');
}
const defaults = require('./config/default.json');
const overrides = require(files[environment]);
const config = { ...defaults, ...overrides };

This is a shallow merge: a nested object in overrides replaces the corresponding nested object from defaults. Define a nested merge policy if required. The files must exist at paths relative to this CommonJS module. Restricting selection avoids building arbitrary paths from an environment variable.

Plain JSON has no comments. YAML, JSON5, or JavaScript configuration can support richer authoring, but require their corresponding parser or loader; JavaScript configuration also executes code.

Use a configuration library when it helps

The config package supplies layered configuration files with documented load precedence. Convict adds schema-driven configuration validation. dotenv-flow provides environment-specific dotenv loading. These tools solve different parts of the problem: choose a clear precedence order and validate types and required values after loading.

For example, a service may use non-secret defaults, deployment-specific overrides, and a small allowlist of environment overrides. Avoid having several independent loaders overwrite the same settings unpredictably.

Deliver and rotate secrets deliberately

Services such as AWS Secrets Manager or HashiCorp Vault manage secrets and access policies. AWS KMS manages cryptographic keys and operations; it is not a drop-in replacement for an application secret store. A secret manager may itself use KMS for encryption.

Use a narrowly scoped workload identity and plan retrieval failures, caching, refresh, and rotation. Fetching only at startup does not automatically adopt a rotated credential. Environment injection and protected mounted files are also valid delivery patterns when their access and lifecycle are controlled.

Docker can receive environment values with -e or an env file. Kubernetes separates ConfigMaps from Secrets, but a Secret is not automatically encrypted merely because its manifest uses base64. Configure access controls and encryption as required. Keep non-secret configuration changes reviewable, and test missing or invalid settings as part of deployment verification.

References

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting