Thuta Learning
IntermediateDevOps & Toolsbeginner

Environment Variables and Secrets

What you'll walk away with

  • Explain the core ideas behind Environment Variables and Secrets
  • Read the diagram and trace how a request or data flows through the architecture
  • Explain what this means for your own project's decisions

Build the mental model

Hardcoding a database connection string or API key directly into source code works at first, but breaks down the moment that code is shared, committed to git, or deployed somewhere else.

Environment variables keep configuration outside the code entirely. A value like DATABASE_URL is set in the running environment, and code simply reads it at runtime through process.env in JavaScript or os.environ in Python.

The same codebase behaves differently depending on where it runs — dev might point at localhost with debugging on, prod points the same variable at a live domain with debugging off. Only the surrounding configuration changes.

  • API keys and access tokens
  • Database passwords and connection strings
  • Signing keys and authentication secrets
Environment Variable
A named configuration value set outside the code, in the environment the application runs in, and read by the code at runtime.
Secret
A sensitive environment variable — an API key, password, or token — that must never be visible to anyone or anything without a legitimate need for it.
text
CODE + ENVIRONMENT CONFIG -> RUNNING APP
----------------------------------------
              +------------------+
Dev Config -->|                  |--> App: localhost DB,
              |    Same Code     |     debug ON
              |                  |
Prod Config-->|                  |--> App: prod DB,
              +------------------+     debug OFF

Connect it to a real scenario

For each piece of configuration, decide whether it belongs on the server only or is safe to reach the browser. Credentials and signing secrets stay server-side; a public API URL or feature flag needs an explicit public-safe prefix before a framework will expose it to client code.

Local development

Keep values in a .env file, and exclude it from version control with .gitignore so secrets never get committed by accident.

Production

Values live in your deployment platform's environment variable settings instead of any file at all.

If a secret is ever accidentally committed to git, rotating it everywhere it's used is not optional — git history keeps old commits indefinitely, so deleting the line in a new commit does not remove it from the project's history.

Never expose server-side secrets to frontend code

Anything that ends up inside client-side JavaScript is downloaded to every visitor's browser and readable by anyone who opens developer tools — no matter how it was built or minified. If a variable lacks your framework's public-safe prefix (or gets imported into a component that ships to the browser), treat it as instantly public. A database password, private API key, or signing secret in frontend code is not hidden — it is published.

Try the working example

javascript
function getConfig(env) {
  return {
    databaseUrl: env.DATABASE_URL || "postgres://localhost:5432/dev_db",
    appUrl: env.APP_URL || "http://localhost:3000",
  };
}

const devEnv = {};
const prodEnv = {
  DATABASE_URL: "postgres://prod-db.internal:5432/app",
  APP_URL: "https://myapp.com",
};

console.log("Dev config:", getConfig(devEnv));
console.log("Prod config:", getConfig(prodEnv));
You should see
Dev config: { databaseUrl: 'postgres://localhost:5432/dev_db', appUrl: 'http://localhost:3000' }
Prod config: { databaseUrl: 'postgres://prod-db.internal:5432/app', appUrl: 'https://myapp.com' }

5-minute try-it

Add a THIRD env state to the code example representing a 'staging' environment with its own DATABASE_URL, and confirm getConfig() returns the right values without touching the function itself — only the environment object changes.

One important caution

Committing a .env file to git, which puts every secret it holds into permanent, recoverable history.

Assuming a variable is safe just because it's read from process.env — if it lacks a public-safe prefix and still ends up bundled into frontend code, it's exposed.

Where should this password live?

Your app needs to connect to a database using a password. Where should that password live?

The Twelve-Factor App — III. ConfigCloud & Deployment

Easy traps

  • Committing a .env file to git, which puts every secret it holds into permanent, recoverable history.
  • Assuming a variable is safe just because it's read from process.env — if it lacks a public-safe prefix and still ends up bundled into frontend code, it's exposed.
  • Never assume that working on localhost means it will work in production -- environment, network, database, and security differences can all bite.

Exercise

Add a THIRD env state to the code example representing a 'staging' environment with its own DATABASE_URL, and confirm getConfig() returns the right values without touching the function itself — only the environment object changes.

You'll know it worked when: Dev config: { databaseUrl: 'postgres://localhost:5432/dev_db', appUrl: 'http://localhost:3000' } Prod config: { databaseUrl: 'postgres://prod-db.internal:5432/app', appUrl: 'https://myapp.com' }

Environment Variables and Secrets | Thuta Learning