Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Deploying the Full App  ›  Lesson

Config Lives in the Environment

Linux Basics 13 minThe same code runs everywhere and reads its settings and secrets from the process's environment variables, which come from an ignored .env file locally and a protected file on the server, while anything built into the client is public.

Config Lives in the Environment

On your laptop, Sprout talks to a local database with a throwaway password. On the live server it must use the real database and a real secret. The code has to be identical in both places, so the difference must come from outside the code. On Linux, that outside is the process's environment.

The rule

Same code everywhere; the environment says where it is running.

Every Linux process carries a set of named text values, its environment variables, inherited from whatever started it. Node exposes them as process.env:

const port = Number(process.env.PORT ?? 3000);
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });

const secret = process.env.JWT_SECRET;
if (!secret) throw new Error('JWT_SECRET is not set');

Reading config this way lets one build run anywhere. Failing loudly at startup when a required value is missing beats failing mysteriously on the first login.

In a Linux shell, export puts a variable into the shell's environment, and every program started from that shell afterwards inherits it:

export DATABASE_URL="postgres://sprout:devpass@localhost:5432/sprout"
printenv DATABASE_URL
node src/index.js

Locally, typing exports gets old, so the values live in a .env file loaded at startup, with node --env-file=.env src/index.js (Node 20.6 and later) or the dotenv package. .env is listed in .gitignore. Instead, commit a .env.example with the variable names and fake values, so a new teammate knows what to fill in.

On the server, the service manager starts the app and hands it the variables, for example from a systemd line such as EnvironmentFile=/etc/sprout/api.env. That file is readable only by its owner (chmod 600) and never enters git.

In the client, be careful. Vite lets client code use variables whose names start with VITE_, and at build time it writes their values straight into the JavaScript bundle, which anyone can read. Client variables are for public values only, such as a support email address. Secrets exist only in the server's environment.

The shortcut

For each config value ask: "Would I be happy to print this on a billboard?" No: it lives only in the server's environment, never in git and never in the client bundle. Yes: it may be a client variable or a committed default.

Sorting Sprout's settingsStep 1 of 4
  1. DATABASE_URL

    It contains the database password. Server environment only: .env locally, the environment file on the server.

The classic slip is committing .env once, "just for now". Deleting it later does not help, because the value stays in git history. If a secret was ever committed, treat it as leaked and replace it.

Drills

Answer fast — trust the rule.

quick drill

Where does a Node program read its environment variables?

quick drill

Which file belongs in git?

quick drill

A developer adds VITE_JWT_SECRET so the client can check tokens itself. What is wrong?

quick drill

After export PORT=8080 in a shell, which processes see PORT?

Remember

  • Same code everywhere; the environment says where it is running.
  • Shortcut: "Would I print it on a billboard?" No means server environment only.
  • Trap: committing .env or putting secrets in VITE_ variables. A committed secret is a leaked secret.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainThe password in the code

A teammate hard-coded Sprout's production database password in server/src/db.js 'just for now' and pushed it. Explain the risks, and walk through how to move the password into the environment, both locally and on the server.