π³ Docker Guide for Node.js & Postgres
node modules generally should be in a volume as they have OS specific binaries so cant copy from host to container directly
- volume mapping -> can lead to hot reload??
This guide covers two workflows:
- Lite Setup: Only the Database is in Docker (Best for local dev)
- Full Setup: Both App and Database are in Docker (Best for production simulation)
π Part 1: The βLiteβ Setup (Recommended)
Use this for daily development. Your Node app runs fast on your laptop, while your DB runs cleanly in a container.
1.1 docker-compose.yml
Create this file in your project root:
version: "3.8"
services:
db:
image: postgres:16-alpine
container_name: my_local_db
restart: always
ports:
- "5432:5432"
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
volumes:
- ./pg-data:/var/lib/postgresql/data
1.2 Environment (.env)
Since the App is on your laptop, it connects to localhost.
DB_USER=myuser
DB_PASSWORD=mypassword
DB_NAME=mydatabase
DATABASE_URL="postgres://myuser:mypassword@localhost:5432/mydatabase"
1.3 Commands
| Goal | Command |
|---|---|
| Start DB | docker-compose up -d |
| Stop DB | docker-compose down |
| Wipe Data (Reset) | docker-compose down then delete pg-data/ folder |
| Run App | npm run dev (Normal local terminal) |
for first time setup, run:
docker-compose up -d --build
v
π’’ Part 2: The βFullβ Setup
Use this to test how your app behaves in a Linux environment or for easy deployment.
2.1 Dockerfile
Create this file to define your Node image.
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]
2.2 docker-compose.yml (App + DB)
version: "3.8"
services:
db:
image: postgres:16-alpine
restart: always
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
volumes:
- ./pg-data:/var/lib/postgresql/data
ports:
- "5432:5432"
app:
build: .
ports:
- "3000:3000"
depends_on:
- db
environment:
DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
volumes:
- .:/app
- /app/node_modules
π‘ Part 3: Tips & Tricks
3.1 Managing Secrets
- Never commit
.envto GitHub. Add it to.gitignore. - Create a
.env.examplewith dummy values for teammates. - Docker Compose automatically reads variables from the
.envfile.
3.2 Installing Packages (Full Setup Only)
If your app is running inside Docker, follow this 2-step process:
Install Inside (For the App):
docker-compose exec app npm install axios
Sync Outside (For VS Code):
npm install
open a shell
docker-compose exec app sh
3.3 Running Scripts
Database Migration:
docker-compose exec app npm run db:migrate
Database Seed:
docker-compose exec app npm run db:seed
Open a Shell:
docker-compose exec app sh
3.4 Git & Lockfiles
- Trust the Docker Lockfile: Docker generates Linux-compatible lockfiles.
- Prefer lockfiles generated by
docker-compose exec app npm install. - On deployment servers, use
npm ciinstead ofnpm install.
3.5 Troubleshooting Networking
Error: connect ECONNREFUSED 127.0.0.1:5432
- Cause: Trying to connect to localhost from inside a container.
- Fix: Change hostname to
db(the service name).
Error: Module not found
- Cause: Installed package locally but not in the container.
- Fix:
docker-compose exec app npm install
β Final Checklist: Which Setup to Use?
| Aspect | Lite Setup | Full Setup |
|---|---|---|
| Speed | β‘οΈ Fast (Native) | π’ Slower (Virtualization) |
| Complexity | π’ Easy | π΄ Moderate |
| Best For | Daily Coding / Solo Dev | Testing Linux / Teams |
| Host | localhost | db |
π― Part 3: Advanced Configurations
3.1 Using Profiles for Optional Services
If you need tools like pgAdmin only for debugging, use Docker Compose Profiles:
version: "3.8"
services:
db:
image: postgres:16-alpine
restart: always
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
volumes:
- ./pg-data:/var/lib/postgresql/data
app:
build: .
ports:
- "3000:3000"
depends_on:
- db
pgadmin:
image: dpage/pgadmin4
profiles: ["debug"]
ports:
- "5050:80"
environment:
PGADMIN_DEFAULT_EMAIL: admin@example.com
PGADMIN_DEFAULT_PASSWORD: admin
Run normally:
docker-compose up
Run with debug tools:
docker-compose --profile debug up
π Quick Reference: Which Method to Use?
| Scenario | Method | Command |
|---|---|---|
| Local Development | Lite Setup | docker-compose up -d |
| Production Testing | Full Setup | docker-compose up |
| Optional Tools | Profiles | docker-compose --profile debug up |
π― Part 4: Common Add-ons
4.1 Database GUI (pgAdmin)
The Problem: You want to see your data without installing heavy desktop apps like DBeaver or TablePlus.
The Docker Solution: Run a web-based viewer alongside your database.
Add this to your docker-compose.yml:
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
pgadmin:
image: dpage/pgadmin4
ports:
- "8080:80"
environment:
PGADMIN_DEFAULT_EMAIL: admin@admin.com
PGADMIN_DEFAULT_PASSWORD: root
depends_on:
- db
Then visit http://localhost:8080 to manage your database.
4.2 Caching (Redis)
The Problem: Installing Redis on Windows is unsupported, and on Mac it runs in the background consuming RAM.
The Docker Solution: Add 4 lines to your compose file for a disposable Redis instance.
services:
redis:
image: redis:alpine
ports:
- "6379:6379"
Usage in Node:
const { createClient } = require("redis");
const client = createClient({ url: "redis://localhost:6379" });