# Docker Compose Complete Guide: From Beginner to Interview Ready

### What is Docker Compose?

Docker Compose is a tool used to define and manage multiple Docker containers using a single YAML file.

Instead of running multiple docker commands manually, Docker Compose allows you to define all services in one configuration file.

Without Compose:

```bash
docker run postgres
docker run redis
docker run backend
docker run frontend
```

With Compose:

```bash
docker compose up
```

Everything starts automatically.

* * *

### Why Do We Need Docker Compose?

Modern applications rarely consist of a single container.

A typical application may contain:

| Service | Purpose |
| --- | --- |
| Frontend | React / Next.js |
| Backend | Node.js / Express |
| Database | PostgreSQL |
| Cache | Redis |
| Reverse Proxy | Nginx |

Managing these individually becomes difficult.

Docker Compose solves this problem.

* * *

### Docker Compose Workflow

```text
compose.yml
      ↓
docker compose up
      ↓
Network Creation
      ↓
Volume Creation
      ↓
Container Creation
      ↓
Application Running
```

Compose automatically creates:

*   Containers
    
*   Networks
    
*   Volumes
    
*   Service communication
    

* * *

### Basic Docker Compose File

```yaml
services:
  app:
    build: .

  db:
    image: postgres:16
```

This starts:

*   One application container
    
*   One PostgreSQL container
    

* * *

### Services

Services are the most important concept in Docker Compose.

Each service represents a container.

```yaml
services:
  frontend:
    build: ./frontend

  backend:
    build: ./backend

  postgres:
    image: postgres:16
```

Three services create three containers.

* * *

### Build

Used when Docker should build an image using a Dockerfile.

```yaml
services:
  backend:
    build: .
```

Equivalent command:

```bash
docker build .
```

Custom path:

```yaml
services:
  backend:
    build:
      context: .
      dockerfile: Dockerfile.prod
```

* * *

### Image

Used when Docker should pull an image from Docker Hub.

```yaml
services:
  postgres:
    image: postgres:16
```

Examples:

```yaml
image: redis:7

image: nginx:latest

image: mongo:7
```

* * *

### Build vs Image

| Feature | Build | Image |
| --- | --- | --- |
| Uses Dockerfile | Yes | No |
| Builds Locally | Yes | No |
| Pulls From Registry | No | Yes |
| Used For Custom Apps | Yes | Yes |

Interview Question:

> When should you use build instead of image?

**Answer**

Use build when creating your own application image. Use image when using pre-built images from Docker Hub.

* * *

### Container Name

Provides a custom container name.

```yaml
services:
  backend:
    container_name: backend-app
```

Without this:

```text
project_backend_1
```

With this:

```text
backend-app
```

* * *

### Ports

Maps host ports to container ports.

```yaml
ports:
  - "3000:3000"
```

Format:

```text
HOST:CONTAINER
```

Example:

```yaml
ports:
  - "8080:3000"
```

Request:

```text
localhost:8080
```

Container receives:

```text
3000
```

Interview Question:

> What is the format of port mapping?

**Answer**

HOST\_PORT

* * *

### Environment Variables

Pass runtime variables.

```yaml
environment:
  NODE_ENV: production
  PORT: 3000
```

Example:

```yaml
services:
  backend:
    environment:
      DATABASE_URL: postgres://user:pass@db:5432/app
```

Access:

```javascript
process.env.DATABASE_URL
```

* * *

### Env File

Store variables in a separate file.

`.env`

```env
DATABASE_URL=mydb
PORT=3000
```

Compose:

```yaml
env_file:
  - .env
```

Benefits:

*   Cleaner configuration
    
*   Easier secret management
    

* * *

### Volumes

Volumes persist data.

```yaml
volumes:
  - postgres-data:/var/lib/postgresql/data
```

Without volumes:

```text
Container Deleted
      ↓
Data Deleted
```

With volumes:

```text
Container Deleted
      ↓
Data Preserved
```

Common use cases:

*   PostgreSQL
    
*   MySQL
    
*   MongoDB
    
*   File uploads
    

* * *

### Named Volumes

```yaml
services:
  postgres:
    image: postgres:16
    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:
```

Docker manages storage automatically.

* * *

### Bind Mounts

Map local folders into containers.

```yaml
volumes:
  - .:/app
```

Benefits:

*   Live code updates
    
*   Faster development
    

Common for Next.js and Node.js projects.

* * *

### Networks

Compose automatically creates a network.

Example:

```yaml
services:
  backend:
  postgres:
```

Backend can connect to database using:

```text
postgres
```

instead of:

```text
localhost
```

Connection string:

```env
DATABASE_URL=postgresql://user:pass@postgres:5432/db
```

Interview Question:

> How do containers communicate inside Docker Compose?

**Answer**

Containers communicate through the automatically created Docker network using service names.

* * *

### Depends On

Controls startup order.

```yaml
services:
  backend:
    depends_on:
      - postgres
```

Docker starts:

```text
Postgres
    ↓
Backend
```

Important:

depends\_on does not wait until the database is ready.

It only starts containers in order.

Interview Question:

> Does depends\_on guarantee database readiness?

**Answer**

No. It only guarantees startup order.

* * *

### Restart Policies

Automatically restart containers.

```yaml
restart: always
```

Options:

```yaml
restart: always

restart: unless-stopped

restart: on-failure

restart: no
```

Common production setting:

```yaml
restart: unless-stopped
```

* * *

### Command

Override Dockerfile CMD.

Dockerfile:

```dockerfile
CMD ["npm", "start"]
```

Compose:

```yaml
command: npm run dev
```

Compose command takes precedence.

* * *

### Healthcheck

Monitors container health.

```yaml
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:3000"]
  interval: 30s
  timeout: 10s
  retries: 3
```

Benefits:

*   Better monitoring
    
*   Automatic recovery
    
*   Production readiness
    

* * *

### Profiles

Run only selected services.

```yaml
services:
  postgres:
    profiles:
      - development
```

Run:

```bash
docker compose --profile development up
```

Useful for:

*   Development
    
*   Testing
    
*   Production
    

* * *

### Real World Example

Node.js + PostgreSQL

```yaml
services:
  backend:
    build: .

    ports:
      - "3000:3000"

    environment:
      DATABASE_URL: postgresql://admin:secret@postgres:5432/app

    depends_on:
      - postgres

  postgres:
    image: postgres:16

    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: app

    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:
```

Start:

```bash
docker compose up
```

Everything starts automatically.

* * *

### Common Docker Compose Commands

Start:

```bash
docker compose up
```

Background mode:

```bash
docker compose up -d
```

Stop:

```bash
docker compose down
```

View logs:

```bash
docker compose logs
```

Follow logs:

```bash
docker compose logs -f
```

List containers:

```bash
docker compose ps
```

Restart:

```bash
docker compose restart
```

Build:

```bash
docker compose build
```

Rebuild:

```bash
docker compose up --build
```

* * *

### Common Interview Questions

#### What is Docker Compose?

A tool used to define and manage multi-container Docker applications using a YAML file.

* * *

#### Dockerfile vs Docker Compose?

| Dockerfile | Docker Compose |
| --- | --- |
| Builds Images | Runs Containers |
| Defines Environment | Defines Services |
| Creates Images | Creates Application Stack |

* * *

#### How do containers communicate?

Through Docker networks using service names.

* * *

#### What is depends\_on?

Defines container startup order.

* * *

#### Does depends\_on wait for readiness?

No.

* * *

#### What is the purpose of volumes?

Persistent data storage.

* * *

#### What is the difference between bind mounts and named volumes?

| Bind Mount | Named Volume |
| --- | --- |
| Uses Host Directory | Managed By Docker |
| Good For Development | Good For Production |

* * *

#### What happens when docker compose down is executed?

Containers and networks are removed.

Volumes remain unless explicitly deleted.

* * *

#### Can Compose build images?

Yes.

Using:

```yaml
build: .
```

* * *

### Production Best Practices

1.  Use explicit image versions.
    

```yaml
image: postgres:16
```

Avoid:

```yaml
image: postgres:latest
```

2.  Use healthchecks.
    
3.  Use named volumes for databases.
    
4.  Store secrets in environment files.
    
5.  Separate development and production configurations.
    
6.  Use restart policies.
    
7.  Avoid hardcoded credentials.
    
8.  Keep services isolated.
    

* * *

### Common Mistakes

| Mistake | Problem |
| --- | --- |
| Using latest tag | Unexpected updates |
| No volumes | Data loss |
| No healthchecks | Difficult monitoring |
| Hardcoded secrets | Security risk |
| Using localhost for DB | Container communication failure |

* * *

### Docker Compose Revision Sheet

| Keyword | Purpose |
| --- | --- |
| services | Define containers |
| build | Build image |
| image | Pull image |
| container\_name | Custom name |
| ports | Port mapping |
| environment | Runtime variables |
| env\_file | External variables |
| volumes | Persistent storage |
| networks | Service communication |
| depends\_on | Startup order |
| restart | Auto restart |
| command | Override CMD |
| healthcheck | Health monitoring |
| profiles | Environment-specific services |

## Docker Compose Interview Scenarios

In real interviews, Docker Compose questions are usually scenario-based.

Interviewers want to evaluate:

*   Service communication
    
*   Networking knowledge
    
*   Volume management
    
*   Environment variables
    
*   Startup dependencies
    
*   Production readiness
    

This guide contains common interview scenarios and their solutions.

* * *

## Scenario 1: Backend + PostgreSQL

### Interview Question

You have a Node.js backend and PostgreSQL database.

Requirements:

*   Backend should run on port 3000
    
*   PostgreSQL should run on port 5432
    
*   Backend should connect to PostgreSQL
    
*   Database data should persist after container restart
    

Write a Docker Compose file.

* * *

### Solution

```yaml
services:
  backend:
    build: .

    ports:
      - "3000:3000"

    environment:
      DATABASE_URL: postgresql://admin:secret@postgres:5432/app

    depends_on:
      - postgres

  postgres:
    image: postgres:16

    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: app

    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:
```

* * *

### Interview Explanation

Backend connects using:

```text
postgres
```

NOT

```text
localhost
```

Because containers communicate through service names.

Correct:

```env
DATABASE_URL=postgresql://admin:secret@postgres:5432/app
```

Wrong:

```env
DATABASE_URL=postgresql://admin:secret@localhost:5432/app
```

* * *

### Follow-Up Questions

#### Why use a volume?

To prevent database data loss.

#### Why use depends\_on?

To ensure PostgreSQL starts before Backend.

#### Does depends\_on wait for database readiness?

No.

* * *

## Scenario 2: Backend + Redis

### Interview Question

Create a Compose file where:

*   Backend runs on port 3000
    
*   Redis runs on port 6379
    
*   Backend uses Redis for caching
    

* * *

### Solution

```yaml
services:
  backend:
    build: .

    ports:
      - "3000:3000"

    environment:
      REDIS_URL: redis://redis:6379

    depends_on:
      - redis

  redis:
    image: redis:7
```

* * *

### Interview Explanation

Backend can access Redis using:

```text
redis
```

because Redis service name becomes DNS hostname.

* * *

### Follow-Up Question

How does Backend discover Redis?

Answer:

Docker Compose automatically creates a network and service names become hostnames.

* * *

## Scenario 3: Frontend + Backend

### Interview Question

You have:

*   React Frontend
    
*   Express Backend
    

Both should communicate through Docker network.

Frontend runs on:

```text
localhost:5173
```

Backend runs on:

```text
localhost:3000
```

* * *

### Solution

```yaml
services:
  frontend:
    build: ./frontend

    ports:
      - "5173:5173"

  backend:
    build: ./backend

    ports:
      - "3000:3000"
```

* * *

### Communication

Frontend should call:

```text
http://backend:3000
```

inside Docker network.

Not:

```text
http://localhost:3000
```

* * *

### Interview Trap

Many candidates use:

```text
localhost
```

This is wrong because each container has its own localhost.

* * *

## Scenario 4: Full Stack Application

### Interview Question

Create a Compose file containing:

*   Next.js
    
*   Express
    
*   PostgreSQL
    
*   Redis
    

All services should communicate.

* * *

### Solution

```yaml
services:
  frontend:
    build: ./frontend

    ports:
      - "3000:3000"

  backend:
    build: ./backend

    ports:
      - "4000:4000"

    environment:
      DATABASE_URL: postgresql://admin:secret@postgres:5432/app

      REDIS_URL: redis://redis:6379

    depends_on:
      - postgres
      - redis

  postgres:
    image: postgres:16

    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: app

    volumes:
      - postgres-data:/var/lib/postgresql/data

  redis:
    image: redis:7

volumes:
  postgres-data:
```

* * *

### Architecture

```text
Frontend
    ↓
Backend
   ↓ ↓
Redis PostgreSQL
```

* * *

### Follow-Up Questions

How many networks are created?

Answer:

One default network.

* * *

How does Backend connect to PostgreSQL?

Answer:

Using hostname:

```text
postgres
```

* * *

How does Backend connect to Redis?

Answer:

Using hostname:

```text
redis
```

* * *

## Scenario 5: Custom Network

### Interview Question

Create two services that communicate using a custom Docker network.

* * *

### Solution

```yaml
services:
  backend:
    build: .

    networks:
      - app-network

  postgres:
    image: postgres:16

    networks:
      - app-network

networks:
  app-network:
```

* * *

### Architecture

```text
backend
     ↓
app-network
     ↑
postgres
```

* * *

### Interview Explanation

Both services belong to the same network.

Therefore:

```text
backend
```

can communicate with:

```text
postgres
```

* * *

## Scenario 6: Multiple Networks

### Interview Question

You have:

*   Frontend
    
*   Backend
    
*   Database
    

Database should NOT be accessible by Frontend.

Design the network architecture.

* * *

### Solution

```yaml
services:
  frontend:
    build: ./frontend

    networks:
      - public

  backend:
    build: ./backend

    networks:
      - public
      - private

  postgres:
    image: postgres:16

    networks:
      - private

networks:
  public:
  private:
```

* * *

### Architecture

```text
Frontend
    |
 Public
    |
Backend
    |
 Private
    |
Postgres
```

* * *

### Why?

Frontend cannot directly access PostgreSQL.

Only Backend can.

This is a common production setup.

* * *

## Scenario 7: Persistent Database

### Interview Question

Your PostgreSQL data disappears after container recreation.

How would you fix it?

* * *

### Solution

```yaml
services:
  postgres:
    image: postgres:16

    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:
```

* * *

### Follow-Up Question

What happens if the container is deleted?

Answer:

Data remains inside the volume.

* * *

## Scenario 8: Environment Variables

### Interview Question

Move all sensitive values out of compose.yml.

* * *

### Solution

.env

```env
POSTGRES_USER=admin
POSTGRES_PASSWORD=secret
POSTGRES_DB=app
```

compose.yml

```yaml
services:
  postgres:
    image: postgres:16

    env_file:
      - .env
```

* * *

### Why?

Better security.

Cleaner configuration.

* * *

## Scenario 9: Health Checks

### Interview Question

Ensure Backend starts only when PostgreSQL becomes healthy.

* * *

### Solution

```yaml
services:
  postgres:
    image: postgres:16

    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]

      interval: 10s

      timeout: 5s

      retries: 5
```

* * *

### Interview Explanation

Health checks allow Docker to monitor service readiness.

* * *

## Scenario 10: Production Ready Compose

### Interview Question

What would you add before deploying to production?

* * *

### Expected Answer

*   Named volumes
    
*   Health checks
    
*   Restart policies
    
*   Environment files
    
*   Fixed image versions
    
*   Separate networks
    
*   Resource limits
    
*   Logging configuration
    

Example:

```yaml
restart: unless-stopped
```

```yaml
healthcheck:
```

```yaml
volumes:
```

```yaml
env_file:
```

* * *

## Most Common Interview Traps

### Trap 1

Using localhost between containers.

Wrong:

```text
localhost
```

Correct:

```text
service-name
```

* * *

### Trap 2

No volume for database.

Result:

```text
Container Deleted
      ↓
Data Lost
```

* * *

### Trap 3

Using latest image tag.

Wrong:

```yaml
image: postgres:latest
```

Correct:

```yaml
image: postgres:16
```

* * *

### Trap 4

Thinking depends\_on waits for readiness.

It does not.

It only controls startup order.

* * *

### Trap 5

Exposing database publicly.

Wrong:

```yaml
ports:
  - "5432:5432"
```

Not always necessary.

Expose only when needed.

* * *

## 15-Minute Interview Revision

| Question | Expected Answer |
| --- | --- |
| How do containers communicate? | Service Name |
| What creates networking? | Docker Compose |
| How to persist data? | Volumes |
| Build vs Image? | Custom vs Existing Image |
| What is depends\_on? | Startup Order |
| Does depends\_on wait? | No |
| How to store secrets? | env\_file |
| Why healthchecks? | Readiness Monitoring |
| Why custom networks? | Isolation |
| Why named volumes? | Data Persistence |

### Conclusion

Docker Compose is used to manage multiple containers as a single application. It simplifies networking, storage, environment configuration, startup order, and deployment. Understanding services, build, image, ports, environment variables, volumes, networks, and depends\_on is sufficient for handling most real-world projects and Docker-related interviews.
