Skip to content
advanced Phase 12 · Docker & Applications

Dockerizing Full Stack Apps

Containerize full stack apps with frontend, backend, and database services.

1h 15m
0 problems
Topic Progress 0%

Service Topology in Compose

Service Topology in Compose

One file describes the whole stack; service names become hostnames on the default network:

services:
  web:
    build: ./web
    ports: ["5173:5173"]
    depends_on: [api]
  api:
    build: ./api
    environment:
      DATABASE_URL: postgres://app:pass@db:5432/app
    depends_on:
      db: { condition: service_healthy }
  db:
    image: postgres:16
    environment: { POSTGRES_PASSWORD: pass, POSTGRES_USER: app }
    volumes: ["pgdata:/var/lib/postgresql/data"]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      retries: 5
volumes:
  pgdata:

Inside api, the host is literally db — no IP bookkeeping. condition: service_healthy turns the health check into an ordering guarantee.

Development versus Production Shapes

Development versus Production Shapes

Dev favors iteration: bind-mount source, run the dev server, let hot reload work.

web:
  volumes: ["./web/src:/app/src"]   # dev only
  command: npm run dev

Prod favors immutability: multi-stage builds compile assets, and nginx serves them; the API runs the built output with NODE_ENV=production. Keep both shapes by overriding with a second compose file (-f compose.yml -f compose.prod.yml), so the base file stays the developer's default and CI never hand-edits YAML. Database migrations run as a one-off service (docker compose run api npm run migrate) before scaling the new API version.

Networking Pitfalls

Networking Pitfalls

The localhost trap. The browser runs on the host, not in the network — fetch("http://api:8080") from React fails because api resolves only inside Compose's DNS. Fix by proxying from the origin: nginx location /api/ { proxy_pass http://api:8080; } or Vite's server.proxy, so the browser talks to /api on its own host.

Ports are per-service. Only web needs a published port; publishing db exposes the database to your machine's whole network.

Secrets in YAML. .env files referenced via env_file keep credentials out of git; rotate anything that has ever been committed.

Interview: draw the three networks/ports involved, explain service DNS versus browser resolution, and describe startup ordering with health gates.

Quiz

1. Why does http://api:8080 work from the api's neighbor but not from the browser?

Question 1 options

2. What does depends_on: { condition: service_healthy } guarantee?

Question 2 options

3. Why publish only the web port?

Question 3 options

Flashcards

Question

Service DNS

Answer

Compose's embedded DNS resolves service names to container IPs on the project network.

Question

service_healthy

Answer

depends_on condition that waits for a passing healthcheck before starting dependents.

Question

Proxy pattern

Answer

Browser calls same-origin /api; nginx or Vite proxies to the api service name.

Question

Override file

Answer

compose.prod.yml layered over compose.yml so dev and prod share one base.

Revision Notes

Key Takeaways

  • 1. Service names are hostnames — inside the network only
  • 2. Gate startup on healthchecks, not on container start
  • 3. Publish ports for user-facing services only

Interview Tips

  • • Draw request flow: browser → web/proxy → api → db and name each hop's DNS
  • • Explain how migrations run relative to a new version going live

Cheat Sheet

web/api/db via service DNS; DATABASE_URL host=db; depends_on condition: service_healthy; browser reaches api via /api proxy; publish web port only.

Continue Learning