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

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 registry
    • docker run -d nginx # Run an Nginx container in detached mode
    • docker ps # List running containers
    • docker 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.

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

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 root inside the container. This is a massive “Checkmark” for security-conscious companies like Visa.

  • Why do you put depends_on: - postgres in 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-alpine instead 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.

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

StateDescriptionCommand
CreatedA container is created but not started.docker create
RunningThe container is actively running.docker start / docker run
PausedThe processes in the container are frozen.docker pause
StoppedThe container has exited, either normally or due to failure.docker stop
KilledThe container was forcefully terminated.docker kill
RemovedThe 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 run is a wrapper command that performs both docker create and docker start in 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, localhost refers 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/