Docker is a containerization platform that ensures consistency across environments. Docker Architecture consists of the Docker Daemon, Client, Images, Containers, and Registries.
Docker Daemon (dockerd)
- Runs in the background as a service.
- Listens for API requests from the Docker Client.
- Responsible for building, running, and managing containers.
- Manages system-wide resources like images, containers, networks, and volumes.
- Check if the Docker Daemon is running:
sudo systemctl status docker
- Check if the Docker Daemon is running:
Docker Client (docker)
- CLI tool used to interact with Docker.
- Sends commands to the Docker Daemon via the API.
- Commands follow this structure:
docker <command> [options] - Example commands:
docker pull nginx# Download image from registrydocker run -d nginx# Run an Nginx container in detached modedocker ps# List running containersdocker stop<container_id> # Stop a running container 3. Docker Images
Docker registries
A Docker registry stores Docker images. Docker Hub is a public registry that anyone can use, and Docker looks for images on Docker Hub by default. You can even run your own private registry.
When you use the docker pull or docker run commands, Docker pulls the required images from your configured registry. When you use the docker push command, Docker pushes your image to your configured registry.
Dockerfile
**F.R.U.N.C.
From, Run, User, Network/Ports, Command**
The 5-Step Core Template
- FROM — Defines the base operating system or language.
- RUN — Installs packages and builds your application.
- COPY — Moves files from your computer into the container.
- EXPOSE — Documents the network port the container uses.
- CMD — Defines the final command that runs the app.
The Chronological Order: You must get the OS (FROM), set the folder (WORKDIR), move your code (COPY), build the app (RUN), and finally start it (CMD).
FROM
- Defines the base operating system or language.
WORKDIR
- Sets the working directory inside the container.
- Example: WORKDIR /usr/src/app
- This ensures all subsequent commands run inside /usr/src/app.
- Example: WORKDIR /usr/src/app
RUN
- Executes commands during image build
- Installs packages and builds your application
- Only works during build phase
- Creates a new Layer
- Best practice: Use && and \ to reduce image layers
- eg. - RUN apt-get update && apt-get install -y curl
COPY & ADD
- Moves files from host (your computer) into the container.
EXPOSE
- Documents the network port the container uses (listens on)
- This does NOT publish the port; you must use -p in docker run.
- Example:
- EXPOSE 8080
CMD
- Defines the final command that runs the app.
ENTRYPOINT
LATER
ENV
- Sets environment variables inside the container.
- Example:
- ENV NODE_ENV=production
VOLUME
- Creates a persistent storage location in the container.
- Example:
- VOLUME /data
LABEL
- Adds metadata to images.
- Example:
- LABEL maintainer=“yourname@example.com”
Multistage Builds
Multi-stage builds help reduce image size by using multiple FROM statements.
- Java applications require a build step (Maven or Gradle) and
- a Java Runtime Environment (JRE).
- The modern standard uses a multi-stage build to keep your final container small and secure.
Security
Dockerfile for Java
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
COPY . .
RUN ./mvnw clean package -DskipTests
# Stage 2: Run (The lightweight "Production" image)
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
# Pull the JAR from the builder stage
COPY --from=builder /app/target/*.jar app.jar
# SECURITY: Never run as root!
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
ENTRYPOINT ["java", "-jar", "app.jar"]-
Multi-stage: You don’t include the Maven source files in your final production image. This makes the image smaller and harder to hack.
-
jre-alpine: You use the JRE (Runtime) instead of the JDK (Development Kit) to reduce the attack surface. -
Non-root user: You don’t run as
rootinside the container. This is a massive “Checkmark” for security-conscious companies like Visa. -
Why do you put
depends_on: - postgresin your order-service?”**- To control service startup order. It ensures the database is initialized before the application attempts to open a connection pool, preventing ‘Connection Refused’ crashes at boot.”
-
Why do we use
eclipse-temurin:21-jre-alpineinstead of a full Linux image?”**- The Staff Answer: “It is a hardened, minimal runtime. It reduces the attack surface by excluding unnecessary binaries, shell tools, and build kits, which significantly lowers security vulnerability scores (CVEs) in our production scan.”
Docker Images
Docker images are read-only templates that contain instructions for creating a container. A Docker image is a snapshot or blueprint of the libraries and dependencies required inside a container for an application to run.
- Blueprints for containers.
- Built using a Dockerfile (a script containing instructions for creating an image).
- Stored locally or in a registry.
It is a lightweight, standalone, executable package that contains everything needed to run a piece of software, including:
- Code
- Runtime
- System libraries
- Dependencies
- Environment variables
- Configuration files
Docker images are immutable, meaning they cannot be changed once created. Instead, modifications require building a new image.
Docker Image Layers
- Docker images are built using layers.
- Layers allow Docker to optimize storage and speed up builds by reusing parts of images.
- Each layer represents an instruction in the Dockerfile (e.g., RUN, COPY, ADD), and Docker caches these layers.
Key Points About Layers
- Base Image Layer: The starting point of an image (e.g., ubuntu:latest, node:18-alpine).
- Intermediate Layers: Created by commands in the Dockerfile (e.g., RUN apt-get update).
- Read-Only Layers: All image layers are read-only.
- Copy-on-Write Mechanism: When a container is created from an image, Docker adds a read write layer on top of the read-only layers, allowing changes.
The Cache Rule:
Least Frequent to Most Frequent
Docker reads a Dockerfile from top to bottom.
- If a line changes, Docker breaks the cache for that line and every single line below it.
- If you put your source code (which changes daily) at the top, Docker will rebuild everything below it from scratch every single time.
- Correct strategy (Option 4): Put your dependencies (
pom.xml/mvn dependency:go-offline) at the top. They rarely change. Put your source code at the bottom. This keeps your builds lightning fast
How Docker Reads the .env File
By default, when you run docker compose up, Docker looks explicitly for a file named exactly .env in the current working directory (the folder where you run the command).
It reads the file line by line, parses the key-value pairs, and temporarily loads them into your shell’s environment context just for that execution.
When Environment File is in a different Location
If your file has a different name (e.g., .env.production) or sits in a different folder, Docker will not find it automatically. You have two primary ways to tell Docker where to look.
Method 1: Using the —env-file Terminal Flag (Recommended)
You can explicitly point Docker to the file’s path right inside your terminal command using the —env-file flag.
-
File in a different folder:
docker compose —env-file ./config/.env up -d -
File with a custom name:
docker compose —env-file .env.prod up -d
[5]
Method 2: Defining it Inside docker-compose.yml
If you want a specific container service to always load a specific file without typing flags in the terminal, use the env_file attribute inside your Compose file.
services:
app:
build: .
ports:
- “8080:8080”
env_file:
- ./config/.env.prod # Path to your custom file
environment:
- SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD}
⚠️ A Critical Difference You Must Know
- The Terminal Flag (—env-file): Substitutes variables inside the docker-compose.yml file itself (like changing ${DB_PASSWORD} to your actual secret).
- The Compose Property (env_file:): Passes all the variables inside that file straight into the application container, but cannot be used to substitute variables inside the docker-compose.yml text.
Docker Containers
- Lightweight, standalone runtime environments created from images.
- Use namespaces and control groups (cgroups) for process isolation.
- Stateless by default (use volumes for persistence).
Container Lifecycle
| State | Description | Command |
|---|---|---|
| Created | A container is created but not started. | docker create |
| Running | The container is actively running. | docker start / docker run |
| Paused | The processes in the container are frozen. | docker pause |
| Stopped | The container has exited, either normally or due to failure. | docker stop |
| Killed | The container was forcefully terminated. | docker kill |
| Removed | The container no longer exists. | docker rm |
Docker Run
The docker run command is used to create and start a new container from an image.
>
docker runis a wrapper command that performs bothdocker createanddocker startin a single step.
It performs these steps internally:
-
Checks if the specified image exists locally
- If not found, it downloads the image from Docker Hub (or another registry).
-
Creates a new container from the image.
- This involves setting up the filesystem, network interfaces, and default configurations.
-
Allocates resources (CPU, memory, networking).
-
Starts the container by executing the default command specified in the image.
Difference between RUN, CMD and EntryPoint
LATER
Docker Networking
Containers in the same docker-compose network talk via their service names (e.g., jdbc:postgresql://postgres:5432/...)
-
Docker Networking: When your app is inside a container,
localhostrefers to the container itself, not your machine. -
Service Discovery: Docker Compose creates a private network where containers can resolve each other by their service name (the names you put under the
services:block in your YAML). -
The Interview Answer: “In a containerized environment, services are discovered via DNS-based resolution provided by the Docker bridge network. Using service names ensures our application configuration remains decoupled from the physical network topology.
TBD : Add more to it
- What is docker bridge n/w
To check
https://www.reddit.com/r/docker/comments/x3c71s/docker\_best\_practice\_host\_or\_bridge\_network/
Volume Mapping
We use volumes: in docker-compose (to persist DB data so it doesn’t vanish when the container restarts).
References
https://aws.amazon.com/compare/the-difference-between-docker-images-and-containers/