Docker & Docker Compose — Interview Questions
Stack context: This system uses Docker Compose with 14 containers: 6 Spring Boot services + 4 PostgreSQL instances + Apache Kafka (KRaft) + Confluent Schema Registry + Redis + Zipkin. All services communicate on a Docker bridge network. Testcontainers is used in integration tests to spin up real infrastructure.
Q1 — What is Docker and what problem does it solve? junior
Answer: Docker is a containerization platform that packages an application and all its dependencies into a portable, isolated container. Containers run consistently across any environment (developer laptop, CI, staging, production).
Problems it solves:
- "Works on my machine": The container includes the exact JDK version, libraries, and config — the same image runs everywhere.
- Dependency isolation: Multiple services can use different JDK/library versions without conflict.
- Reproducible environments: Dev, staging, and production use the same image.
- Fast startup: Containers start in seconds (no OS boot like VMs).
- Efficient resources: Containers share the host OS kernel (no hypervisor overhead).
Docker vs VM:
| Virtual Machine | Docker Container | |
|---|---|---|
| OS | Full guest OS per VM | Shares host kernel |
| Size | GBs | MBs |
| Startup time | Minutes | Seconds |
| Isolation | Strong (hypervisor) | Process-level namespaces |
| Resource use | High | Low |
Q2 — What is a Docker image and how is it different from a container? junior
Answer:
- Image: A read-only template containing the application code, runtime, libraries, and config. Built from a
Dockerfile. Stored in layers. - Container: A running (or stopped) instance of an image. Containers are ephemeral — when deleted, any state not stored in a volume is lost.
Image (read-only) → Container (image + writable layer)
Analogy: Image = class definition; Container = object instance.
Key commands:
# Build image from Dockerfile in current directory
docker build -t order-service:1.0.0 .
# Run a container from the image
docker run -d -p 8083:8083 --name order-service order-service:1.0.0
# List running containers
docker ps
# See all containers (including stopped)
docker ps -a
# Remove container
docker rm -f order-service
# List images
docker images
# Remove image
docker rmi order-service:1.0.0
Q3 — What is a Dockerfile and what are the key instructions? junior
Answer: A Dockerfile is a text file containing instructions for building a Docker image. Each instruction creates a new layer.
# Base image
FROM eclipse-temurin:21-jre
# Set working directory
WORKDIR /app
# Copy application JAR (built externally with Maven)
COPY target/order-service-1.0.0-SNAPSHOT.jar app.jar
# Expose port (documentation only — doesn't publish the port)
EXPOSE 8083
# Environment variables with defaults
ENV SPRING_PROFILES_ACTIVE=docker
ENV JAVA_OPTS="-Xms256m -Xmx512m"
# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
CMD curl -f http://localhost:8083/actuator/health || exit 1
# Command to run
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
Key instructions:
FROM: Base image.WORKDIR: Set working directory for subsequent instructions.COPY/ADD: Copy files into image.RUN: Execute shell command during build (creates a layer).ENV: Set environment variable.EXPOSE: Document port (does not publish).ENTRYPOINT: Default executable (not overridable easily).CMD: Default arguments (overridable viadocker run).
Q4 — What are Docker image layers and why do they matter for build performance? senior
Answer:
Every RUN, COPY, and ADD instruction creates a new layer. Layers are cached — if a layer's content doesn't change, Docker reuses the cached layer.
Build cache optimization:
# SLOW — copy everything first; any source change invalidates all subsequent layers
COPY . .
RUN ./mvnw package -DskipTests
# FAST — dependencies change rarely; copy pom.xml first and cache the mvn install layer
COPY pom.xml .
COPY src/main/resources ./src/main/resources
RUN ./mvnw dependency:go-offline
# Source changes only invalidate from here
COPY src/main/java ./src/main/java
RUN ./mvnw package -DskipTests
Layer ordering rule: Put infrequently changing instructions first, frequently changing instructions last.
Layer inspection:
docker history order-service:1.0.0
docker inspect order-service:1.0.0 | jq '.[0].RootFS.Layers'
This system: The pre-built JAR is copied in a single COPY instruction (Maven builds externally). In a CI pipeline, use multi-stage builds for clean separation.
Q5 — What is a multi-stage build and why is it used? senior
Answer:
Multi-stage builds use multiple FROM instructions to keep the final image small by discarding build tools and intermediate files.
Java multi-stage build:
# ---- Build stage ----
FROM maven:3.9.6-eclipse-temurin-21 AS builder
WORKDIR /build
# Cache dependencies first
COPY pom.xml .
RUN mvn dependency:go-offline -q
# Build application
COPY src ./src
RUN mvn package -DskipTests -q
# ---- Extract layers (Spring Boot layered JAR) ----
FROM eclipse-temurin:21-jre AS extractor
WORKDIR /app
COPY --from=builder /build/target/order-service-*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
# ---- Final runtime image ----
FROM eclipse-temurin:21-jre
WORKDIR /app
# Copy Spring Boot layers (most stable first = better caching)
COPY --from=extractor /app/dependencies/ ./
COPY --from=extractor /app/spring-boot-loader/ ./
COPY --from=extractor /app/snapshot-dependencies/ ./
COPY --from=extractor /app/application/ ./
EXPOSE 8083
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]
Benefits:
- Build stage uses
maven:3.9.6(~700 MB) — not in final image. - Final image uses
eclipse-temurin:21-jre(~200 MB) — no Maven, no source code. - Spring Boot layered JAR: application code changes only invalidate the
applicationlayer, notdependencies.
Q6 — What is Docker Compose and what problem does it solve? junior
Answer:
Docker Compose is a tool for defining and running multi-container applications. Instead of running docker run commands manually for each container, you define all services in a docker-compose.yml and start everything with one command.
This system's docker-compose.yml (14 containers):
docker compose up -d --build # start all 14 containers in detached mode
docker compose down # stop and remove containers
docker compose logs -f order-service # follow logs
docker compose ps # show container status
Benefits:
- Single command to start the entire system.
- Services, networks, and volumes defined declaratively.
- Automatic service dependency management (
depends_on). - Shared environment configuration.
Q7 — What are the key sections of a docker-compose.yml file? junior
Answer:
version: '3.9'
services: # Container definitions
order-service:
image: order-service:latest # or build: ./order-service
ports:
- "8083:8083" # host:container
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://order-db:5434/orderdb
- SPRING_KAFKA_BOOTSTRAP_SERVERS=kafka:9092
depends_on:
order-db:
condition: service_healthy # wait for health check
kafka:
condition: service_healthy
networks:
- ecommerce-network
volumes:
- ./logs:/app/logs # mount host directory
order-db:
image: postgres:16
environment:
POSTGRES_DB: orderdb
POSTGRES_PASSWORD: ${DB_PASSWORD} # from .env file
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
networks:
ecommerce-network:
driver: bridge
volumes:
order-db-data: # named volume (persists across restarts)
driver: local
Q8 — What is the difference between depends_on and health checks? junior
Answer:
depends_on without health check: Starts the dependent container when the dependency container starts — NOT when it's ready to accept connections. PostgreSQL container starts in ~1 second but is ready for queries after ~5-10 seconds.
Problem: order-service starts right after postgres container starts, but Postgres isn't ready → Flyway migration fails.
Solution: depends_on + condition: service_healthy:
services:
order-service:
depends_on:
order-db:
condition: service_healthy # wait until health check passes
kafka:
condition: service_healthy
order-db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d orderdb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s # grace period before health check starts
Health check conditions:
service_started: Just container started (default fordepends_on).service_healthy: Health check passes.service_completed_successfully: Container exited with code 0 (for init containers).
Q9 — What are Docker volumes and what are the different types? junior
Answer: Volumes persist data beyond the container lifecycle. When a container is deleted, its writable layer (container data) is lost — volumes survive.
Three types:
1. Named volumes (managed by Docker, best for databases):
volumes:
order-db-data:
services:
order-db:
volumes:
- order-db-data:/var/lib/postgresql/data # named volume
2. Bind mounts (host path mapped into container):
services:
order-service:
volumes:
- ./config:/app/config # mount host directory (read-only)
- ./logs:/app/logs # write logs to host
3. tmpfs mounts (in-memory, Linux only):
services:
redis:
tmpfs:
- /data # Redis data in memory — lost on container restart
When to use:
- Named volumes: Database data, Kafka logs, persistent state.
- Bind mounts: Config files, log access from host, development hot-reload.
- tmpfs: Sensitive data that shouldn't touch disk (temp secrets).
Q10 — How does Docker networking work and what are the network modes? senior
Answer: Containers communicate via Docker networks. Within the same network, containers reach each other by service name (DNS-resolved by Docker).
Network modes:
| Mode | Description | Use case |
|---|---|---|
bridge (default) |
Private virtual network; NAT to host | Multi-container apps (this system) |
host |
Container uses host's network namespace | Maximum performance, no port mapping |
none |
No networking | Isolated containers |
overlay |
Multi-host networking (Docker Swarm) | Distributed cluster |
Custom bridge network (best practice):
networks:
ecommerce-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
Service DNS: Within ecommerce-network:
order-servicereacheskafka:9092(service name = DNS hostname).order-servicereachesorder-db:5432(notlocalhost!).order-servicereachesredis:6379.
Port mapping (8083:8083): Makes the container's port 8083 accessible on the host as 8083. Without port mapping, the service is reachable only from within the Docker network.
Q11 — How do you configure environment variables securely in Docker Compose? senior
Answer:
Option 1: .env file (recommended for development):
# .env file (in docker-compose directory, git-ignored!)
DB_PASSWORD=supersecret
REDIS_PASSWORD=redispass
JWT_SECRET=verylongsecretkey
services:
order-db:
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD} # interpolated from .env
Option 2: env_file directive (per-service config):
services:
order-service:
env_file:
- ./order-service/.env
Option 3: Docker secrets (for production/Swarm):
services:
order-db:
secrets:
- db_password
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
external: true # created with: docker secret create db_password <file>
Security rules:
- Never hardcode secrets in
docker-compose.yml(committed to git). - Always add
.envto.gitignore. - Provide
.env.examplewith placeholder values for documentation.
Q12 — How do you build and push a Docker image to a registry? junior
Answer:
# Build image with tag
docker build -t myregistry.io/kafka-redis/order-service:1.0.0 \
-t myregistry.io/kafka-redis/order-service:latest \
./order-service
# Login to registry
docker login myregistry.io -u username -p password
# (better: use docker credential helpers, not CLI passwords)
# Push image
docker push myregistry.io/kafka-redis/order-service:1.0.0
docker push myregistry.io/kafka-redis/order-service:latest
# Pull image on another machine
docker pull myregistry.io/kafka-redis/order-service:1.0.0
Tagging strategy:
latest: Points to the most recent build. Avoid in production (mutable tag).1.0.0: Semantic version. Immutable — best for production.git-sha(e.g.,abc1234): Tied to specific commit. Best for CI/CD traceability.
GitHub Container Registry:
docker tag order-service:latest ghcr.io/username/order-service:latest
docker push ghcr.io/username/order-service:latest
Q13 — What is the difference between ENTRYPOINT and CMD in a Dockerfile? junior
Answer:
ENTRYPOINT |
CMD |
|
|---|---|---|
| Purpose | Main executable | Default arguments |
| Override | docker run --entrypoint |
docker run <image> <cmd> |
| Combination | ENTRYPOINT + CMD = executable + args | If ENTRYPOINT set, CMD becomes args to ENTRYPOINT |
# Pattern 1: ENTRYPOINT only
ENTRYPOINT ["java", "-jar", "app.jar"]
# docker run myimage --server.port=9090 → java -jar app.jar --server.port=9090
# Pattern 2: ENTRYPOINT + CMD (CMD provides defaults)
ENTRYPOINT ["java"]
CMD ["-jar", "app.jar"]
# docker run myimage -jar other.jar → java -jar other.jar (CMD overridden)
# Pattern 3: Shell form (can use $JAVA_OPTS)
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
Best practice for Spring Boot:
ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
-XX:+UseContainerSupport (default in JDK 11+): JVM respects Docker memory limits instead of using the host's total RAM.
Q14 — How does container networking work between services in Docker Compose? junior
Answer: When services are defined in the same Docker Compose file and connected to the same network, they can reach each other using the service name as the hostname.
Example (this system):
services:
order-service:
# Can reach kafka at kafka:9092
environment:
SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:9092
SPRING_DATASOURCE_URL: jdbc:postgresql://order-db:5434/orderdb
SPRING_REDIS_HOST: redis
kafka:
image: confluentinc/cp-kafka:7.6.1
ports:
- "9092:9092" # accessible from host at localhost:9092
# accessible from containers at kafka:9092
order-db:
image: postgres:16
ports:
- "5434:5432" # host port 5434 maps to container port 5432
Port mapping for host access:
- Host (e.g., DBeaver, Kafka tool) connects to
localhost:5434. - Container (order-service) connects to
order-db:5432(container port, not host port).
Common mistake: Configuring Spring Boot with localhost:5432 — this looks up localhost inside the container, not the host machine.
Q15 — What is docker compose up --build vs docker compose up? junior
Answer:
| Command | Behavior |
|---|---|
docker compose up |
Uses existing images; builds if image doesn't exist |
docker compose up --build |
Force-rebuilds images before starting (picks up code changes) |
docker compose build |
Build images without starting containers |
docker compose up -d |
Start in detached mode (background) |
docker compose down |
Stop and remove containers (volumes preserved) |
docker compose down -v |
Stop, remove containers AND volumes (delete DB data) |
docker compose restart order-service |
Restart a single service |
docker compose pull |
Pull latest images from registry |
Workflow when you change code:
# Rebuild and restart only the changed service
docker compose up -d --build order-service
# Or rebuild all
docker compose up -d --build
This system's build script (build.sh/build.ps1):
- Runs
./mvnw package -DskipTestsfor all services. - Runs
docker compose up -d --buildto rebuild images and restart.
Q16 — How do you debug a running container? junior
Answer:
# View live logs
docker compose logs -f order-service
# View last 100 lines
docker compose logs --tail=100 order-service
# Execute a shell inside a running container
docker compose exec order-service bash
# or (if bash not available)
docker compose exec order-service sh
# Inside the container — check process, connections
ps aux
netstat -tulpn
curl http://localhost:8083/actuator/health
# Inspect container config and network
docker inspect order-service
# Check environment variables
docker compose exec order-service env | grep SPRING
# Copy file from container to host
docker cp order-service:/app/logs/application.log ./
Remote JVM debugging (add to ENTRYPOINT):
ENTRYPOINT ["java", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", "-jar", "app.jar"]
ports:
- "5005:5005" # expose debug port
Connect IDE remote debugger to localhost:5005.
Q17 — What is Docker's resource limiting and why is it important? senior
Answer: Without resource limits, one container can monopolize CPU or memory, starving other containers (or crashing the host).
Docker Compose resource limits:
services:
order-service:
deploy:
resources:
limits:
cpus: '1.0' # max 1 CPU core
memory: 512m # max 512 MB RAM
reservations:
cpus: '0.25' # guaranteed 0.25 CPU
memory: 256m # guaranteed 256 MB
JVM and container memory:
Without -XX:+UseContainerSupport (default ON in JDK 11+), JVM reads the host's total RAM (e.g., 32 GB) and sizes heap accordingly, ignoring the Docker memory limit → OOMKilled.
With container support:
# JVM uses 75% of container memory limit
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
# 512m container → 384m heap max
CPU throttling: cpus: '0.5' limits the container to 50% of one core using Linux CFS (completely fair scheduler) quota.
Q18 — What is a Docker health check and how does it work? senior
Answer: A health check periodically runs a command inside the container to determine if the application is healthy and ready for traffic.
In Dockerfile:
HEALTHCHECK --interval=30s \
--timeout=5s \
--start-period=60s \
--retries=3 \
CMD curl -sf http://localhost:8083/actuator/health | grep -q '"status":"UP"' || exit 1
In docker-compose.yml:
services:
order-service:
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:8083/actuator/health"]
interval: 30s
timeout: 5s
start_period: 60s # grace period — don't check for 60s after start
retries: 3 # mark unhealthy after 3 consecutive failures
Health states:
starting: Withinstart_period— not yet checked.healthy: Last N checks passed.unhealthy: Lastretrieschecks failed.
Integration with depends_on:
order-service:
depends_on:
order-db:
condition: service_healthy
order-service waits until order-db is healthy before starting.
Q19 — How does Testcontainers use Docker and what are its advantages over embedded alternatives? senior
Answer: Testcontainers starts real Docker containers during tests and provides connection details (host, port) for the application to connect.
@SpringBootTest
@Testcontainers
class OrderServiceIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@Container
static KafkaContainer kafka = new KafkaContainer(
DockerImageName.parse("confluentinc/cp-kafka:7.6.1"));
@DynamicPropertySource
static void overrideProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.kafka.bootstrap-servers", kafka::getBootstrapServers);
}
Advantages over embedded alternatives:
| Embedded H2 | Testcontainers PostgreSQL | |
|---|---|---|
| DB dialect | H2 (different SQL) | Real PostgreSQL |
| Flyway migrations | Some incompatibilities | 100% compatible |
| JSON/JSONB | Not supported | Full support |
| Triggers, procedures | Limited | Full support |
| Test confidence | Lower | Higher |
This system: Integration tests use Testcontainers with real PostgreSQL, real Kafka, and real Redis — same versions as production.
Q20 — What is Docker image scanning and why is it important? senior
Answer: Image scanning analyzes Docker images for known CVE (Common Vulnerability Exposures) vulnerabilities in base OS packages and application dependencies.
Common tools:
# Trivy (free, fast)
trivy image order-service:1.0.0
# Docker Scout (built into Docker Desktop)
docker scout cves order-service:1.0.0
# Snyk
snyk container test order-service:1.0.0
Sample output:
CRITICAL CVE-2024-XXXX openssl/libssl3 3.1.4 3.1.5 Fix available
HIGH CVE-2024-YYYY libc-bin 2.36 2.37 Fix available
CI/CD integration:
# GitHub Actions
- name: Scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: 'order-service:latest'
severity: 'CRITICAL,HIGH'
exit-code: '1' # fail CI on CRITICAL/HIGH
Minimize attack surface:
- Use minimal base images (
eclipse-temurin:21-jrevs:21-jdk). - Use distroless images:
gcr.io/distroless/java21-debian12— no shell, no package manager. - Regularly rebuild images to pick up base OS patches.
Q21 — What is the difference between docker compose v1 and v2? junior
Answer:
| Docker Compose v1 | Docker Compose v2 | |
|---|---|---|
| Command | docker-compose (hyphen) |
docker compose (space) |
| Binary | Separate Python binary | Plugin built into Docker CLI |
| Installation | pip install docker-compose |
Bundled with Docker Desktop |
| Performance | Slower (Python) | Faster (Go) |
depends_on condition |
Not supported | service_healthy supported |
profiles |
Limited | Full support |
This system: Uses docker compose (v2) with depends_on: condition: service_healthy.
Profiles (useful for development vs production):
services:
zipkin:
profiles: ["observability"] # only started with --profile observability
order-service:
# no profile — always started
docker compose --profile observability up -d # starts all + Zipkin
docker compose up -d # starts core services only
Q22 — How do you handle log aggregation for containerized services? senior
Answer:
Containers write logs to stdout/stderr. Docker captures these and makes them available via docker logs. For production, aggregate logs centrally.
Docker logging drivers:
services:
order-service:
logging:
driver: "json-file" # default — writes to host filesystem
options:
max-size: "100m" # rotate at 100MB
max-file: "5" # keep 5 files
# Or send directly to log aggregator:
logging:
driver: "fluentd"
options:
fluentd-address: "localhost:24224"
tag: "order-service.{{.ID}}"
ELK Stack integration (Elasticsearch, Logstash, Kibana):
- Services log JSON to stdout.
- Filebeat (sidecar or host agent) ships logs to Logstash.
- Logstash parses and indexes to Elasticsearch.
- Kibana provides search and visualization.
Structured logging (JSON format) in Spring Boot:
logging:
structured:
format.console: ecs # ECS JSON format (Spring Boot 3.4+)
Every log line becomes a JSON object with traceId, spanId, service.name, making log correlation trivial.
Q23 — What is Docker's --network host mode and when is it used? senior
Answer:
In host network mode, the container shares the host's network namespace — no network isolation, no port mapping needed.
# Container uses host network directly
docker run --network host order-service:latest
# Listens on host's port 8083 directly (no docker NAT)
When to use:
- Performance-critical applications where Docker NAT overhead is unacceptable.
- Network monitoring tools that need to see all host interfaces.
- When the application needs to bind to a specific host interface.
Security risk: The container can access all host network interfaces and services on localhost. No network isolation from the host.
Linux only: --network host is not supported on Docker Desktop for Mac/Windows (which runs Docker in a Linux VM).
This system: Uses custom bridge networks (ecommerce-network) — best for microservices where services need to find each other by name.
Q24 — How do you perform zero-downtime deployments with Docker Compose? senior
Answer: Docker Compose is primarily designed for development. Zero-downtime deployment requires careful orchestration:
Rolling restart (single service update):
# Build new image
docker compose build order-service
# Scale to 2 instances
docker compose up -d --scale order-service=2
# Wait for new instance to be healthy
# Remove old instance (requires sticky routing or stateless service)
docker compose up -d --scale order-service=1
Blue-green (external load balancer required):
services:
order-service-blue:
image: order-service:1.0.0
labels:
traefik.enable: "true"
traefik.http.routers.order-blue.rule: "Host(`order.example.com`)"
order-service-green:
image: order-service:1.1.0
labels:
traefik.enable: "false" # disabled initially
Production recommendation: Use Kubernetes (kubectl rollout) for true zero-downtime deployments. Docker Compose is not designed for production orchestration at scale.
Q25 — What is docker system prune and when should you use it? junior
Answer:
docker system prune removes unused Docker resources (stopped containers, unused images, unused networks, unused volumes).
# Remove stopped containers, unused networks, dangling images
docker system prune
# Also remove unused volumes (WARNING: deletes data!)
docker system prune --volumes
# Remove all unused images (not just dangling)
docker system prune -a
# Check disk usage before pruning
docker system df
Types of cleanup:
docker container prune # remove stopped containers
docker image prune # remove dangling (untagged) images
docker image prune -a # remove all unused images
docker volume prune # remove unused volumes
docker network prune # remove unused networks
CI/CD best practice: Run docker system prune -f in CI after each build to prevent disk exhaustion from accumulated build artifacts. Use with caution in production (volumes contain data).
Q26 — How does Kafka run in Docker in KRaft mode without ZooKeeper? senior
Answer: Traditional Kafka requires ZooKeeper for cluster metadata management. KRaft (Kafka Raft) embeds cluster coordination directly into Kafka brokers, eliminating the ZooKeeper dependency.
This system's Kafka config:
kafka:
image: confluentinc/cp-kafka:7.6.1
environment:
KAFKA_NODE_ID: 1
KAFKA_PROCESS_ROLES: broker,controller # single node = both roles
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
CLUSTER_ID: MkU3OEVBNTcwNTJENDM2Qk # must be stable UUID
volumes:
- kafka-data:/var/lib/kafka/data # persist topic data
Key listeners:
PLAINTEXT://kafka:9092: For producers/consumers (internal Docker network).CONTROLLER://kafka:9093: KRaft controller communication (internal).- For external access (from host): Add
PLAINTEXT_HOST://0.0.0.0:29092advertised aslocalhost:29092.
Benefits: No ZooKeeper container needed — reduces 14 containers to 13 (this system still uses 14 because of Schema Registry, Redis, Zipkin).
Q27 — What are Docker Compose profiles and how do you use them? junior
Answer: Profiles allow you to selectively start subsets of services. Services without a profile are always started; services with a profile only start when that profile is activated.
services:
# Core services — always started
order-service:
image: order-service:latest
kafka:
image: confluentinc/cp-kafka:7.6.1
# Optional observability stack
zipkin:
image: openzipkin/zipkin:3
profiles: ["observability"]
prometheus:
image: prom/prometheus:latest
profiles: ["observability"]
grafana:
image: grafana/grafana:latest
profiles: ["observability"]
# Development tools
pgadmin:
image: dpage/pgadmin4:latest
profiles: ["tools"]
Usage:
# Core services only
docker compose up -d
# Core + observability
docker compose --profile observability up -d
# Core + observability + tools
docker compose --profile observability --profile tools up -d
Env variable alternative:
COMPOSE_PROFILES=observability,tools docker compose up -d
Q28 — How do you implement a container init sequence for dependent services? senior
Answer:
depends_on: condition: service_healthy handles most cases. For complex init sequences, use init containers or wait scripts.
Custom wait script:
# wait-for-it.sh in image
COPY wait-for-it.sh /wait-for-it.sh
RUN chmod +x /wait-for-it.sh
ENTRYPOINT ["/wait-for-it.sh", "kafka:9092", "--", "java", "-jar", "app.jar"]
Kubernetes init container pattern (Docker Compose equivalent):
services:
schema-migrator: # run migrations first, exit 0
image: flyway:10
command: migrate
depends_on:
order-db:
condition: service_healthy
order-service:
depends_on:
schema-migrator:
condition: service_completed_successfully
kafka:
condition: service_healthy
Best practice: Let Spring Boot's Flyway run migrations on startup (current approach). The service itself handles the init sequence. Only use separate migrators for shared schemas or complex multi-service migration coordination.
Q29 — How do you optimize Docker image size? senior
Answer: Large images increase pull times, storage costs, and attack surface.
Strategies:
1. Use minimal base images:
# 700 MB
FROM openjdk:21
# 200 MB
FROM eclipse-temurin:21-jre
# 50 MB (no shell, no package manager)
FROM gcr.io/distroless/java21-debian12
2. Multi-stage builds: Separate build tools from runtime (see Q5).
3. Minimize layers: Combine related RUN commands:
# 3 layers
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
# 1 layer
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
4. .dockerignore: Exclude unnecessary files from build context:
target/
.git/
*.md
src/test/
5. Spring Boot layered JAR (see Q5): Separate dependencies from application code for better caching.
Size analysis:
docker history order-service:latest --no-trunc # see layer sizes
dive order-service:latest # interactive layer explorer
Q30 — What are the security best practices for Docker in production? senior
Answer:
1. Run as non-root user:
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser # never run as root
2. Use read-only filesystem (where possible):
services:
order-service:
read_only: true
tmpfs:
- /tmp # only /tmp is writable
3. Drop Linux capabilities:
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE # only if binding to port < 1024
4. Scan images for CVEs (see Q20): Integrate trivy in CI pipeline.
5. Use specific image tags (not latest): postgres:16.3 instead of postgres:latest.
6. Limit network exposure: Only publish necessary ports. Internal services should not have ports: mappings.
7. Secret management: Never pass secrets via environment variables for production. Use Docker secrets, Vault, or cloud secret managers.
8. Resource limits: Prevent resource exhaustion attacks (see Q17).
9. Container registry security: Use private registries, enable vulnerability scanning, sign images (Docker Content Trust).
10. Rootless Docker: Run Docker daemon as non-root (Podman alternative).