Dockerfile Complete Guide: From Beginner to Interview Ready
Hi there! I'm Aditya, a passionate Full-Stack Developer driven by a love for turning concepts into captivating digital experiences. With a blend of creativity and technical expertise, I specialize in crafting user-friendly websites and applications that leave a lasting impression. Let's connect and bring your digital vision to life!
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:
docker run postgres
docker run redis
docker run backend
docker run frontend
With Compose:
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
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
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.
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.
services:
backend:
build: .
Equivalent command:
docker build .
Custom path:
services:
backend:
build:
context: .
dockerfile: Dockerfile.prod
Image
Used when Docker should pull an image from Docker Hub.
services:
postgres:
image: postgres:16
Examples:
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.
services:
backend:
container_name: backend-app
Without this:
project_backend_1
With this:
backend-app
Ports
Maps host ports to container ports.
ports:
- "3000:3000"
Format:
HOST:CONTAINER
Example:
ports:
- "8080:3000"
Request:
localhost:8080
Container receives:
3000
Interview Question:
What is the format of port mapping?
Answer
HOST_PORT
Environment Variables
Pass runtime variables.
environment:
NODE_ENV: production
PORT: 3000
Example:
services:
backend:
environment:
DATABASE_URL: postgres://user:pass@db:5432/app
Access:
process.env.DATABASE_URL
Env File
Store variables in a separate file.
.env
DATABASE_URL=mydb
PORT=3000
Compose:
env_file:
- .env
Benefits:
- Cleaner configuration
- Easier secret management
Volumes
Volumes persist data.
volumes:
- postgres-data:/var/lib/postgresql/data
Without volumes:
Container Deleted
↓
Data Deleted
With volumes:
Container Deleted
↓
Data Preserved
Common use cases:
- PostgreSQL
- MySQL
- MongoDB
- File uploads
Named Volumes
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.
volumes:
- .:/app
Benefits:
- Live code updates
- Faster development
Common for Next.js and Node.js projects.
Networks
Compose automatically creates a network.
Example:
services:
backend:
postgres:
Backend can connect to database using:
postgres
instead of:
localhost
Connection string:
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.
services:
backend:
depends_on:
- postgres
Docker starts:
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.
restart: always
Options:
restart: always
restart: unless-stopped
restart: on-failure
restart: no
Common production setting:
restart: unless-stopped
Command
Override Dockerfile CMD.
Dockerfile:
CMD ["npm", "start"]
Compose:
command: npm run dev
Compose command takes precedence.
Healthcheck
Monitors container health.
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.
services:
postgres:
profiles:
- development
Run:
docker compose --profile development up
Useful for:
- Development
- Testing
- Production
Real World Example
Node.js + PostgreSQL
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:
docker compose up
Everything starts automatically.
Common Docker Compose Commands
Start:
docker compose up
Background mode:
docker compose up -d
Stop:
docker compose down
View logs:
docker compose logs
Follow logs:
docker compose logs -f
List containers:
docker compose ps
Restart:
docker compose restart
Build:
docker compose build
Rebuild:
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:
build: .
Production Best Practices
- Use explicit image versions.
image: postgres:16
Avoid:
image: postgres:latest
Use healthchecks.
Use named volumes for databases.
Store secrets in environment files.
Separate development and production configurations.
Use restart policies.
Avoid hardcoded credentials.
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
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:
postgres
NOT
localhost
Because containers communicate through service names.
Correct:
DATABASE_URL=postgresql://admin:secret@postgres:5432/app
Wrong:
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
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:
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:
localhost:5173
Backend runs on:
localhost:3000
Solution
services:
frontend:
build: ./frontend
ports:
- "5173:5173"
backend:
build: ./backend
ports:
- "3000:3000"
Communication
Frontend should call:
http://backend:3000
inside Docker network.
Not:
http://localhost:3000
Interview Trap
Many candidates use:
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
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
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:
postgres
How does Backend connect to Redis?
Answer:
Using hostname:
redis
Scenario 5: Custom Network
Interview Question
Create two services that communicate using a custom Docker network.
Solution
services:
backend:
build: .
networks:
- app-network
postgres:
image: postgres:16
networks:
- app-network
networks:
app-network:
Architecture
backend
↓
app-network
↑
postgres
Interview Explanation
Both services belong to the same network.
Therefore:
backend
can communicate with:
postgres
Scenario 6: Multiple Networks
Interview Question
You have:
- Frontend
- Backend
- Database
Database should NOT be accessible by Frontend.
Design the network architecture.
Solution
services:
frontend:
build: ./frontend
networks:
- public
backend:
build: ./backend
networks:
- public
- private
postgres:
image: postgres:16
networks:
- private
networks:
public:
private:
Architecture
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
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
POSTGRES_USER=admin
POSTGRES_PASSWORD=secret
POSTGRES_DB=app
compose.yml
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
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:
restart: unless-stopped
healthcheck:
volumes:
env_file:
Most Common Interview Traps
Trap 1
Using localhost between containers.
Wrong:
localhost
Correct:
service-name
Trap 2
No volume for database.
Result:
Container Deleted
↓
Data Lost
Trap 3
Using latest image tag.
Wrong:
image: postgres:latest
Correct:
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:
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.