Friday, September 18, 2026

3..env

 

Complete .env Guide


1. What is .env

.env = environment variables file
     = a file that stores sensitive or environment-specific values
       outside your actual code

Simple text file. Just key=value pairs.

DATABASE_URL=postgres://user:pass@localhost:5432/mydb
JWT_SECRET=mysecretkey123
PORT=5000

2. Where .env is Used — React, Node or Both

Both use .env but for different purposes.

main-folder/
├── react/
│   └── .env          ← React's own .env
├── nodejs/
│   └── .env          ← Node's own .env
└── docker-compose.yml

React .env — Frontend Config Only

REACT_APP_API_URL=https://yourdomain.com/api
REACT_APP_GOOGLE_MAPS_KEY=AIzaSyXXXXXXXX
REACT_APP_APP_NAME=MyApp

Rule: React env variables MUST start with REACT_APP_
Otherwise React ignores them completely.

Node .env — Backend + Database + Secrets

PORT=5000
DATABASE_URL=postgres://user:pass@db:5432/mydb
JWT_SECRET=supersecretkey
EMAIL_USER=noreply@yourdomain.com
EMAIL_PASS=emailpassword123
RAZORPAY_KEY=rzp_live_XXXXXXXX
RAZORPAY_SECRET=XXXXXXXXXXXXXXX
NODE_ENV=production

3. What Each .env Variable Does

Node .env breakdown

PORT=5000
# Which port your Node server runs on

DATABASE_URL=postgres://user:pass@db:5432/mydb
# Full connection string to Postgres
# user     = postgres username
# pass     = postgres password
# db       = docker service name (hostname)
# 5432     = postgres port
# mydb     = database name

JWT_SECRET=supersecretkey
# Used to sign and verify login tokens
# If leaked → anyone can fake login as any user

NODE_ENV=production
# Tells your app which environment it is running in
# Changes behavior — logging, error messages, optimizations

EMAIL_USER=noreply@yourdomain.com
EMAIL_PASS=emailpassword123
# SMTP credentials for sending emails

RAZORPAY_KEY=rzp_live_XXXXXXXX
RAZORPAY_SECRET=XXXXXXXXXXXXXXX
# Payment gateway credentials

React .env breakdown

REACT_APP_API_URL=https://yourdomain.com/api
# The base URL React uses to call your Node API
# In development → http://localhost:5000/api
# In production  → https://yourdomain.com/api

REACT_APP_GOOGLE_MAPS_KEY=AIzaSyXXXXXXXX
# Public API keys that browser needs directly

4. How .env is Used in Code

In Node (server.js)

// First install dotenv package
// npm install dotenv

// At top of server.js
require('dotenv').config()

// Now use anywhere in your code
const PORT = process.env.PORT
const db = process.env.DATABASE_URL
const secret = process.env.JWT_SECRET

// Example usage
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`)
})

// Database connection
const pool = new Pool({
    connectionString: process.env.DATABASE_URL
})

// JWT signing
const token = jwt.sign(payload, process.env.JWT_SECRET)

In React (any component)

// No extra package needed
// React reads .env automatically at build time

const API_URL = process.env.REACT_APP_API_URL

// Example usage
const response = await fetch(`${API_URL}/users`)

// In development hits → http://localhost:5000/api/users
// In production hits  → https://yourdomain.com/api/users

5. How .env Helps in Production

The Problem Without .env

// BAD — values hardcoded in code
const pool = new Pool({
    host: 'localhost',
    password: 'mypassword123',    // visible to everyone
    database: 'mydb'
})

// pushed to git → anyone who sees your repo
// sees your database password

The Solution With .env

// GOOD — values come from environment
const pool = new Pool({
    connectionString: process.env.DATABASE_URL
    // actual password never appears in code
    // never pushed to git
})

Production Benefits

Local .env                    Production .env
──────────────────            ──────────────────────────
DATABASE_URL=localhost         DATABASE_URL=prod-db-server
NODE_ENV=development           NODE_ENV=production
JWT_SECRET=devkey123           JWT_SECRET=Xk9#mP2$qL8@nR5!
PORT=5000                      PORT=5000

Same code runs in both environments
Just .env values change
No code changes needed

6. .env and .gitignore — Critical Step

.env must NEVER be pushed to git.

# Create .gitignore in each folder
nano .gitignore
# Inside .gitignore
.env
node_modules/
build/
git push
    ↓
.gitignore blocks .env from uploading
    ↓
Git repo has NO passwords or secrets
    ↓
Safe to make repo public or share with team

What gets pushed vs what stays local

Gets pushed to git          Stays on machine only
──────────────────          ──────────────────────
server.js                   .env
package.json                node_modules/
Dockerfile                  build/
docker-compose.yml
.gitignore
.env.example                ← this gets pushed (see below)

7. .env.example — Industry Standard Practice

You push a .env.example file to git so teammates know what variables are needed — but with no real values.

# nodejs/.env.example
PORT=
DATABASE_URL=
JWT_SECRET=
NODE_ENV=
EMAIL_USER=
EMAIL_PASS=
RAZORPAY_KEY=
RAZORPAY_SECRET=
# react/.env.example
REACT_APP_API_URL=
REACT_APP_GOOGLE_MAPS_KEY=

New developer joins team:

git clone repo
cp .env.example .env
# fill in real values
# app runs immediately

8. If .env is NOT Used — Pros and Cons

Pros (almost none)

✓ Slightly simpler for a throwaway script
✓ One less file to manage

Cons (serious problems)

✗ Passwords hardcoded in code
  → visible to anyone with repo access

✗ Code pushed to GitHub with real credentials
  → bots scan GitHub and steal credentials within minutes

✗ Different values for dev and production
  → must manually change code every deployment
  → high risk of deploying wrong values

✗ Team members see production passwords
  → security risk

✗ Payment keys, JWT secrets in plain code
  → major security breach risk

✗ Cannot change password without changing code
  → requires new git commit and deployment

9. How to Create .env — Step by Step

Local Development

# Go into nodejs folder
cd nodejs

# Create .env file
touch .env

# Open and add variables
nano .env
PORT=5000
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
JWT_SECRET=devlocalsecretkey
NODE_ENV=development
EMAIL_USER=test@gmail.com
EMAIL_PASS=testpassword
# Go into react folder
cd ../react

# Create .env file
touch .env
nano .env
REACT_APP_API_URL=http://localhost:5000/api
REACT_APP_APP_NAME=MyApp

On Production Server

# SSH into server
ssh user@your-server-ip

# Go to your project
cd /home/user/your-repo/nodejs

# Create production .env
nano .env
PORT=5000
DATABASE_URL=postgres://user:strongpassword@db:5432/mydb
JWT_SECRET=Xk9#mP2$qL8@nR5!vB3&wZ7
NODE_ENV=production
EMAIL_USER=noreply@yourdomain.com
EMAIL_PASS=realemailpassword
RAZORPAY_KEY=rzp_live_XXXXXXXX
RAZORPAY_SECRET=XXXXXXXXXXXXXXX

10. .env with Docker — How It Connects

Option 1 — Docker reads .env file directly

# docker-compose.yml
services:
  backend:
    build: ./nodejs
    env_file:
      - ./nodejs/.env        ← docker reads this file
    ports:
      - "5000:5000"

  db:
    image: postgres:15
    env_file:
      - ./nodejs/.env        ← postgres gets credentials from same file

Option 2 — Hardcode in docker-compose (not recommended)

services:
  backend:
    environment:
      - PORT=5000
      - JWT_SECRET=mysecret   ← secret visible in docker-compose

Option 1 is always better — secrets stay in .env, not in docker-compose.


11. Industry Standard Summary

Rule 1:  .env never goes to git
Rule 2:  .env.example always goes to git
Rule 3:  Production .env has stronger secrets than dev
Rule 4:  Never hardcode passwords, keys, URLs in code
Rule 5:  Each environment has its own .env
         (local, staging, production)
Rule 6:  Rotate secrets regularly (change passwords periodically)
Rule 7:  Minimum access — .env only contains what that service needs

12. Complete Picture

YOUR CODE (git repo)              YOUR SECRETS (.env)
────────────────────              ──────────────────────
server.js                         PORT=5000
  process.env.PORT          ←───  DATABASE_URL=postgres://...
  process.env.JWT_SECRET    ←───  JWT_SECRET=Xk9#mP2$...
  process.env.DATABASE_URL  ←───  NODE_ENV=production

Code is public safe               .env stays private
Push to git freely                Never pushed to git
Same code everywhere              Different per environment

One codebase. Multiple environments. Zero hardcoded secrets. That is the purpose of .env.

Nginx flow on production

 

Complete Nginx Configuration on Production Server — Full Flow


1. What is Nginx Here

Before Nginx:
Browser → directly hits Node on port 5000 (not professional, not secure)

After Nginx:
Browser → Nginx (port 80/443) → forwards to React/Node containers

Nginx sits in front of everything. Users never see ports like 3000 or 5000.


2. SSH Into Production Server

ssh user@your-server-ip

3. Install Nginx

# Update packages
sudo apt update

# Install nginx
sudo apt install nginx -y

# Check version (confirms install worked)
nginx -v
# Output: nginx version: nginx/1.24.0

# Check nginx is running
sudo systemctl status nginx
# Output: Active: active (running)

4. Understand the Nginx Folder Structure

/etc/nginx/
├── nginx.conf                  ← master config file (don't touch this)
├── sites-available/            ← all your project configs live here
│   └── myproject               ← your config file (disabled until linked)
├── sites-enabled/              ← nginx actually reads from here
│   └── myproject → (symlink)   ← points to sites-available/myproject
└── snippets/                   ← reusable config pieces (ssl etc)

Key concept:

  • You write config in sites-available/
  • Nginx only reads from sites-enabled/
  • You connect them with a symlink — this is how you enable/disable a site without deleting config

5. Create Your Project Config File

sudo nano /etc/nginx/sites-available/myproject

Paste this inside

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    # React frontend
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # Node backend API
    location /api/ {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Save and exit:

  • Press Ctrl + X
  • Press Y
  • Press Enter

6. What Each Line Means

listen 80;
# Nginx listens on port 80 (HTTP)
# All browser requests come in through here

server_name yourdomain.com www.yourdomain.com;
# Which domain this config applies to
# If request domain matches this → use this config block

location / {
# Any request starting with /  (homepage, /about, /contact)
# Goes to React container

    proxy_pass http://localhost:3000;
    # Forward this request to React running on port 3000

    proxy_http_version 1.1;
    # Use HTTP 1.1 (supports persistent connections)

    proxy_set_header Host $host;
    # Tell the container which domain was requested

    proxy_set_header X-Real-IP $remote_addr;
    # Pass the real user IP to your app (not nginx's IP)
}

location /api/ {
# Any request starting with /api/  (api/users, /api/login)
# Goes to Node container

    proxy_pass http://localhost:5000;
    # Forward to Node running on port 5000
}

7. Enable the Config (Symlink)

# Create symlink from sites-available to sites-enabled
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled/

# Verify symlink was created
ls -l /etc/nginx/sites-enabled/
# Output: myproject -> /etc/nginx/sites-available/myproject

8. Remove Default Nginx Page

# Default nginx page blocks your config if not removed
sudo rm /etc/nginx/sites-enabled/default

9. Test and Reload Nginx

# Test config for syntax errors
sudo nginx -t
# Output:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

# Reload nginx with new config (no downtime)
sudo systemctl reload nginx

# Verify nginx still running
sudo systemctl status nginx

10. Add SSL (HTTPS) — Certbot

Port 443 (HTTPS) setup using free SSL from Let's Encrypt.

# Install certbot
sudo apt install certbot python3-certbot-nginx -y

# Get SSL certificate (auto updates your nginx config)
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

# Follow prompts:
# Enter email → agree terms → choose redirect HTTP to HTTPS

Certbot automatically updates your nginx config to:

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$host$request_uri;
    # Redirects all HTTP → HTTPS automatically
}

server {
    listen 443 ssl;
    server_name yourdomain.com www.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /api/ {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

11. Full Request Flow After Nginx Is Configured

User types yourdomain.com
       ↓
DNS resolves → your server IP
       ↓
Request hits server port 80
       ↓
Nginx receives request
— is it HTTP? → redirect to HTTPS (port 443)
       ↓
Request hits port 443
       ↓
Nginx checks /etc/nginx/sites-enabled/
— finds myproject config
— matches server_name yourdomain.com
       ↓
Nginx checks the URL path
       ↓
       ├── path is /           → proxy_pass → React port 3000
       │                               ↓
       │                       React serves frontend
       │                               ↓
       │                       Browser renders page
       │
       └── path is /api/...    → proxy_pass → Node port 5000
                                       ↓
                               Node processes request
                                       ↓
                               Node queries Postgres (port 5432)
                               (internal Docker network only)
                                       ↓
                               Postgres returns data
                                       ↓
                               Node sends JSON response
                                       ↓
                               React updates UI

12. Useful Nginx Commands You'll Use Regularly

# Test config before applying
sudo nginx -t

# Reload after config changes (no downtime)
sudo systemctl reload nginx

# Full restart (downtime — use only if reload fails)
sudo systemctl restart nginx

# Stop nginx
sudo systemctl stop nginx

# Start nginx
sudo systemctl start nginx

# Check nginx logs (debug errors)
sudo tail -f /var/log/nginx/error.log
sudo tail -f /var/log/nginx/access.log

# Check which ports nginx is listening on
sudo netstat -tlnp | grep nginx

13. Complete Picture — What Lives Where

PRODUCTION SERVER
│
├── /etc/nginx/
│   ├── sites-available/myproject  ← your nginx config
│   └── sites-enabled/myproject    ← symlink (enables it)
│
├── /home/user/your-repo/          ← git cloned project
│   ├── react/
│   ├── nodejs/
│   └── docker-compose.yml
│
└── Docker containers running:
    ├── React    → port 3000
    ├── Node     → port 5000
    └── Postgres → port 5432 (internal only)

INTERNET
    ↓
yourdomain.com → Server IP
    ↓
port 80  → Nginx → redirect to 443
port 443 → Nginx → routes to containers
    ↓              ↓
  React          Node → Postgres
 port 3000      port 5000

14. Update Flow When You Change Code

# On production server
cd /home/user/your-repo

# Pull latest code
git pull origin main

# Rebuild and restart Docker containers
docker-compose up --build -d

# Nginx does NOT need to restart
# It keeps forwarding to same ports 3000 and 5000
# Docker handles the new code inside containers

Nginx config only changes if you add a new domain or new route. Code changes never touch Nginx.

NGINX, Docker setup in project folder local

 

Complete Full Flow


1. Folder Structure

main-folder/
├── react/
│   ├── Dockerfile
│   ├── package.json
│   └── src/
├── nodejs/
│   ├── Dockerfile
│   ├── package.json
│   └── server.js
└── docker-compose.yml

2. What You Write Locally (One Time Setup)

react/Dockerfile

FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]

nodejs/Dockerfile

FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]

docker-compose.yml

services:
  frontend:
    build: ./react
    ports:
      - "3000:3000"

  backend:
    build: ./nodejs
    ports:
      - "5000:5000"
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/mydb
    depends_on:
      - db

  db:
    image: postgres:15
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: mydb
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

3. Push to Git

# On your local machine
git init
git add .
git commit -m "initial commit"
git push origin main

4. SSH Into Production Server

ssh user@your-server-ip

5. On Production Server — One Time Setup

Install Docker

sudo apt update
sudo apt install docker.io docker-compose -y

Install Nginx

sudo apt install nginx -y

Clone Your Project

cd /home/user
git clone https://github.com/yourname/your-repo.git
cd your-repo

6. Start Docker Containers

docker-compose up --build -d

What happens here:

Docker reads docker-compose.yml
       ↓
Builds React image  →  runs on port 3000
Builds Node image   →  runs on port 5000
Pulls Postgres image → runs internally
       ↓
All 3 containers running on Docker internal network
       ↓
Node connects to Postgres using hostname "db"
React connects to Node using /api/ routes

Verify containers are running

docker ps

You should see 3 containers — frontend, backend, db


7. Configure Nginx on Production

Create config file

sudo nano /etc/nginx/sites-available/myproject

Paste this config

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    # React frontend
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # Node backend API
    location /api/ {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Enable the config

# Symlink to sites-enabled
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled/

# Test config for errors
sudo nginx -t

# Reload nginx
sudo systemctl reload nginx

8. Point Your Domain to Server

Go to your domain registrar / DNS provider and add:

A Record    @     →   your-server-ip
A Record    www   →   your-server-ip

DNS takes 5–30 minutes to propagate.


9. Full Request Flow When User Types Domain

User types yourdomain.com in browser
       ↓
DNS resolves yourdomain.com → your server IP
       ↓
Request hits server on port 80
       ↓
Nginx receives the request
       ↓
Nginx checks /etc/nginx/sites-enabled/myproject
— matches server_name yourdomain.com
       ↓
       ↓— request is /          → proxy_pass → React container port 3000
       ↓— request is /api/...   → proxy_pass → Node container port 5000
       ↓
React container serves the frontend
       ↓
Browser renders the page
       ↓
User clicks something (login, fetch data)
       ↓
React sends API call to yourdomain.com/api/users
       ↓
Nginx forwards to Node on port 5000
       ↓
Node processes request → queries Postgres container
       ↓
Postgres returns data → Node sends response
       ↓
React receives response → updates UI

10. When You Push Code Updates

# On production server
cd /home/user/your-repo
git pull origin main
docker-compose up --build -d

That's it — Docker rebuilds the containers with new code.


Complete Picture in One Diagram

INTERNET
    ↓
yourdomain.com
    ↓
DNS → Server IP
    ↓
PORT 80/443
    ↓
NGINX (reverse proxy)
    ↓              ↓
location /      location /api/
    ↓              ↓
REACT           NODE JS
port 3000       port 5000
                    ↓
              POSTGRES DB
              (internal Docker network)
              port 5432

What Lives Where

Thing Location
Your code Local machine → Git → /home/user/your-repo on server
Dockerfiles Inside your git repo
docker-compose.yml Inside your git repo
Nginx config /etc/nginx/sites-available/ on server (NOT in git)
Postgres data Docker volume pgdata on server (persists across restarts)
Running containers Docker on production server