Docker for Full-Stack Apps
Part of the Full Stack Developer Roadmap
In short
'It works on my machine' gets worse with three moving parts: React, Express and Postgres. Docker Compose starts all three with one command, the same way on every laptop.
Containerizing both your frontend and backend (and often the database) so your whole stack runs identically on any machine — usually orchestrated together with Docker Compose for local development.

Think of it like this
Stalls in one food court
A food court has separate stalls, each with its own stove and staff, but they share one walkway and one power line, and the mall opens them in order. Each container is a stall, the Compose network is the walkway, and docker compose up is the mall opening for the day.
Watch it happen
docker compose up, in order
1/5Compose starts Postgres first, with a named volume so the data survives restarts.
The big ideas
What you need to know
One container per service
web, api and db each get their own image and process. Rebuild or restart one without touching the others.
Service names are hostnames
Inside the Compose network the API connects to db:5432. localhost inside a container means that same container.
Volumes keep data
Without a volume, removing the db container loses your rows. A named volume keeps them between runs.
Wait for healthy
depends_on with condition: service_healthy stops the API from crashing because Postgres isn't ready yet.
See it in code
Walk through the code
1services:2 db:3 image: postgres:174 environment: { POSTGRES_PASSWORD: dev }5 healthcheck: { test: ["CMD", "pg_isready", "-U", "postgres"], interval: 5s }6 volumes: ["pgdata:/var/lib/postgresql/data"]7 api:8 build: ./api9 ports: ["4000:4000"]10 environment: { DATABASE_URL: "postgres://postgres:dev@db:5432/postgres" }11 depends_on: { db: { condition: service_healthy } }12 web:13 build: ./web14 ports: ["3000:3000"]15 depends_on: [api]16volumes: { pgdata: {} }1/4Postgres with a dev password, a readiness check and a named volume for its data folder.
compose.yaml at the repo root. The browser runs on your machine, outside the Compose network, so the React app still calls localhost:4000. Only the API uses the db hostname.
Save yourself the pain
Common mistakes
Oops
Using localhost:5432 in the API's DATABASE_URL inside Docker.
Do this instead
Use the service name, db:5432. Inside a container, localhost is the container itself.
Oops
Baking secrets into the image with ENV in the Dockerfile.
Do this instead
Pass them at runtime through environment or env_file in Compose, and keep .env out of the image with .dockerignore.
Your mission
One command, whole stack
Quick check
Inside the api container, which host in DATABASE_URL reaches the Postgres container?
Go deeper
- 1Write Dockerfiles for both your frontend and backend.
- 2Use Docker Compose to run frontend, backend and database together with one command.
- 3Confirm a teammate could clone your repo and run the whole stack with zero manual setup.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.