Environment Variables vs Remote Configuration: When to Use Which

“Just put it in an environment variable” is common advice. And often it’s correct—environment variables are simple, secure, and universally supported. But sometimes they’re the wrong tool.

In this article, we’ll compare environment variables with remote configuration and establish clear guidelines for when to use each.

Похожее
map::count function in C++
C++ offers a range of functions to navigate its containers. One such function, `count`, helps find if a key exists in a `std::map`. Dive in to learn how to use it, when it's handy, and see it in action.
go

How environment variables work

Environment variables are key-value pairs available to a process:

const databaseUrl = process.env.DATABASE_URL
const apiKey = process.env.API_KEY
const logLevel = process.env.LOG_LEVEL ?? 'INFO'

They’re set outside your code—in shell scripts, Docker Compose files, or cloud platform settings:

# docker-compose.yml
services:
  app:
    environment:
      - DATABASE_URL=postgresql://localhost/mydb
      - LOG_LEVEL=DEBUG

Environment variables are:

  • Read once at process start
  • Static for the lifetime of the process
  • Scoped to a single process (and its children)

How remote configuration works

Remote configuration fetches values from an external server:

import { Replane } from '@replanejs/sdk'

interface Configs {
  'rate-limit': number
  'new-feature-enabled': boolean
}

const replane = new Replane<Configs>()

await replane.connect({
  sdkKey: process.env.REPLANE_SDK_KEY!,
  baseUrl: 'https://cloud.replane.dev',
})

const rateLimit = replane.get('rate-limit')
const featureEnabled = replane.get('new-feature-enabled')

Remote configuration is:

  • Read at runtime (continuously updated)
  • Dynamic (changes without restart)
  • Shared across all instances

What’s the key difference between environment variables and remote configuration?

Environment variables are more secure
Remote configuration uses less memory
Environment variables are static, remote config is dynamic
Environment variables are faster to read

When to use environment variables

Environment variables are the right choice for:

Похожее
string::substr in C++: Extracting Parts of Strings
C++ offers various methods to work with strings. Among them, the substr function stands out as a way to extract specific portions of a string. In this article, we'll dive into the substr function, see how it works, and explore its numerous applications.
go

Secrets and credentials

API keys, database passwords, and encryption keys belong in environment variables (or a secrets manager):

// Good: secrets in env vars
const stripeKey = process.env.STRIPE_API_KEY
const dbPassword = process.env.DATABASE_PASSWORD

Why? Secrets are sensitive and should:

  • Never appear in dashboards or logs
  • Be rotated through secure channels
  • Have strict access controls

Remote config systems aren’t designed for secret management.

Connection strings and endpoints

Database URLs and service endpoints rarely change at runtime:

// Good: connection info in env vars
const databaseUrl = process.env.DATABASE_URL
const redisUrl = process.env.REDIS_URL
const apiGateway = process.env.API_GATEWAY_URL

These values are tied to infrastructure. When they change, you typically need to restart connections anyway.

Environment-specific settings

Settings that differ between dev/staging/production but don’t change within an environment:

// Good: environment config in env vars
const environment = process.env.ENVIRONMENT ?? 'development'
const debugMode = process.env.DEBUG === 'true'
Похожее
string::size in C++: Measuring String Length
C++ offers a variety of tools to work with strings, and one of the most essential functions is string::size. In this article, we'll dive deep into this function, understand how it measures strings, and explore its real-world applications.
go

Build-time configuration

Settings needed at build time (not runtime):

# Build with specific settings
NEXT_PUBLIC_API_URL=https://api.example.com npm run build

When to use remote configuration

Remote configuration is the right choice for:

Feature flags

Toggles that need to change instantly without deploys:

// Good: feature flags in remote config
if (replane.get('new-checkout-enabled')) {
  showNewCheckout()
}

You want to enable a feature for 10% of users, watch metrics, then increase to 100%—all without deploying.

Похожее
vector::size in C++ (With Examples)
In C++, the std::vector container is a dynamic array that can resize itself automatically. A common need is to determine how many elements a vector currently holds. Enter the size function! This article will break down how the size function works, show you some practical examples, and give insights into related features of vectors.
go

Operational parameters

Settings you tune based on real-world conditions:

// Good: operational tuning in remote config
const rateLimit = replane.get('api-rate-limit')
const cacheTtl = replane.get('cache-ttl-seconds')
const batchSize = replane.get('worker-batch-size')

During a traffic spike, you might need to lower rate limits. During a database slowdown, you might increase timeouts. Remote config lets you respond in seconds.

Kill switches

Emergency controls to disable features:

// Good: kill switch in remote config
if (replane.get('payments-enabled', { default: true })) {
  processPayment()
} else {
  showMaintenanceMessage()
}

When payments break at 2am, you flip a switch instead of emergency-deploying.

Per-user or per-tenant settings

Different customers get different values:

// Good: per-tenant config in remote config
const maxUsers = replane.get('max-users', {
  context: { tenant: tenant.id }
})

Enterprise customers might get 10,000 users while free tier gets 10.

Where should you store your database connection password?

Environment variable or secrets manager
Remote configuration
Hardcoded in the application
In a JSON config file
Похожее
srand in C++: initializing the random number generator
The srand function is used in C++ for initializing the random number generator. This article will explore how to use this function with examples. At the end of the article, there are exercises for further.
go

Decision framework

Here’s a simple flowchart for deciding where to put configuration:

Is it a secret? ──────Yes──────► Environment variable
      │                            (or secrets manager)
      No
      ▼
Does it need to change ──Yes──► Remote configuration
without restarting?
      │
      No
      ▼
Environment variable

Or as a table:

| Configuration Type | Where to Store | |-------------------|----------------| | API keys, passwords | Env var / Secrets manager | | Database URLs | Environment variable | | Feature flags | Remote config | | Rate limits, timeouts | Remote config | | Kill switches | Remote config | | Build-time settings | Environment variable | | Per-user settings | Remote config |

Combining both approaches

In practice, you’ll use both. Here’s a typical pattern:

import { Replane } from '@replanejs/sdk'

// Static config from environment (secrets, connections)
const DATABASE_URL = process.env.DATABASE_URL!
const STRIPE_KEY = process.env.STRIPE_API_KEY!
const ENVIRONMENT = process.env.ENVIRONMENT ?? 'development'

interface Configs {
  'rate-limit': number
  'feature-enabled': boolean
}

// The SDK key itself comes from env var
const replane = new Replane<Configs>()

await replane.connect({
  sdkKey: process.env.REPLANE_SDK_KEY!,
  baseUrl: 'https://cloud.replane.dev',
})

// Dynamic config from remote (features, tuning)
function getRateLimit(): number {
  return replane.get('rate-limit', { default: 100 })
}

function isFeatureEnabled(name: keyof Configs, userContext?: Record<string, unknown>): boolean {
  if (userContext) {
    return replane.get(name, { context: userContext, default: false }) as boolean
  }
  return replane.get(name, { default: false }) as boolean
}

Notice that even the remote config SDK key comes from an environment variable—you use env vars for the truly static, secret values.

Common mistakes

Похожее
vector::erase in C++ (With Examples)
C++ offers several ways to manage elements in its containers. The erase function is a popular way to remove elements from the vector. In this article, we'll dive deep into using this function, understand its intricacies, and look at practical examples.
go

Putting secrets in remote config

// Bad: secrets in remote config
const stripeKey = replane.get('stripe-api-key')

Remote config dashboards show values in plain text. Logs might capture them. Use env vars or a secrets manager for secrets.

Putting everything in env vars

// Bad: dynamic values as env vars require restarts
const FEATURE_NEW_CHECKOUT = process.env.FEATURE_NEW_CHECKOUT === 'true'
const RATE_LIMIT = parseInt(process.env.RATE_LIMIT ?? '100', 10)

If you’re frequently changing these values and restarting to pick up changes, they should be in remote config.

No defaults for remote config

// Bad: throws if config server unavailable
const rateLimit = replane.get('rate-limit')

// Good: has default
const rateLimit = replane.get('rate-limit', { default: 100 })

Your app should work (perhaps degraded) even if remote config is temporarily unavailable.

Похожее
The map::find in C++: Efficiently Locating Elements
C++ boasts a rich collection of libraries, and the map container is among its gems. A key function within this container is map::find, which is essential for locating elements swiftly. In this article, we'll dive into the purpose of this function, explore its mechanics, and run through a practical example.
go

Tools for remote configuration

If you decide to use remote configuration, here are some options:

  • Replane — Open-source, real-time updates via SSE, self-hosted or cloud
  • LaunchDarkly — Enterprise feature flags
  • AWS AppConfig — Managed by AWS, good for AWS-heavy environments
  • Consul — Service mesh with KV store

For self-hosting and open-source, Replane is worth considering—it handles versioning, rollback, and has SDKs for multiple languages.

Summary

Use environment variables for:

  • Secrets and credentials
  • Connection strings and endpoints
  • Environment-specific (dev/staging/prod) settings
  • Build-time configuration

Use remote configuration for:

  • Feature flags
  • Operational tuning (rate limits, timeouts)
  • Kill switches
  • Per-user or per-tenant settings

The key question: “Does this need to change without restarting the application?” If yes, use remote config. If no, environment variables are simpler.

Exercises

  1. Audit your configuration: Look at an application you’ve worked on. Categorize each config value as “should be env var” or “should be remote config”.

  2. Migrate a value: Take one value that’s currently in an environment variable but should be dynamic, and implement it using remote configuration.

  3. Design a config architecture: For a new application, design the configuration strategy. What goes in env vars? What goes in remote config? Document your decisions.

Discussion

© 2026, codelessons.dev