Lesson 4 of 30
A real app is rarely one container — it's a frontend, an API, a database, maybe a cache, all needing to find and talk to each other. Compose is how you describe that whole system in one file instead of a page of docker run commands.
13 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 3
In a Compose file, service `web` needs to reach service `api`. Which URL should `web`'s code actually use?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Docker Compose describes a multi-container application declaratively, in one docker-compose.yml (or compose.yaml) file — every service, its image or build context, its ports, its environment variables, and how services depend on each other. docker compose up reads that file and starts the whole system with one command, instead of a sequence of manually-ordered docker run invocations with matching flags typed by hand every time.
services:
web:
build: .
ports:
- "3000:3000"
depends_on:
- api
environment:
- API_URL=http://api:4000
api:
build: ./api
ports:
- "4000:4000"
environment:
- DATABASE_URL=postgres://db:5432/app
depends_on:
- db
db:
image: postgres:16
environment:
- POSTGRES_PASSWORD=devpassword
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
Three services declared in one Compose file: web, api, db.
Notice API_URL=http://api:4000 — not localhost:4000, and not an IP address. Compose creates a private network for the whole project by default, and every service is reachable from every other service by its service name as a DNS hostname. web reaches api by literally requesting http://api:4000, because Compose's built-in DNS resolves the name api to that container's address on the shared network — no manual network configuration required.
depends_on controls start order, not readiness. It waits for a dependency's container to start, not for the service inside it to actually be ready to accept connections — a database container can be "started" while Postgres is still initializing. A real system adds an actual readiness check (a healthcheck, or retry logic in the dependent service) rather than trusting depends_on alone to mean "ready."db-data above) persist across docker compose down, but docker compose down -v deletes them — a genuinely destructive flag that's easy to run out of habit and only notice after the database is empty.docker-compose.override.yml (loaded automatically alongside the base file) or multiple -f flags let a team keep one shared base configuration and layer environment-specific differences (a debug port, a bind-mounted source folder for hot reload) on top."How do your frontend and backend containers actually find each other in local dev?" tests whether a candidate understands Compose's networking model or is just pattern-matching a YAML file they've copied before. The specific, correct answer — service names resolve as hostnames on Compose's private network — is a concrete, checkable fact, not a vibe.