Chaturmind
LearnDSASystem DesignInterview PrepDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth
  • Java Interview Prep

Company

  • About
  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

Β© 2026 Chaturmind. All rights reserved.

πŸ‘₯10,104visitors

Built for engineers who want to go deep.


← Cloud & DevOps Fundamentals

Docker Fundamentals

  • Docker Fundamentals
  • Multi-Stage Builds
  • Networking & Storage
  • docker-compose for Local Development
  • Health Checks & Production Best Practices

Kubernetes Essentials

  • Core Objects
  • Services & Ingress
  • Config & Secrets
  • Probes & Autoscaling
  • Deployments & Rollouts

AWS Cloud Fundamentals

  • EC2
  • S3
  • RDS
  • IAM
  • VPC Basics

DevOps Practices

  • CI/CD Pipeline Design
  • Deployment Strategies
  • Infrastructure as Code Awareness
  • Monitoring in Production
  • Cost Awareness
Chaturmind
← Cloud & DevOps Fundamentals

Docker Fundamentals

  • Docker Fundamentals
  • Multi-Stage Builds
  • Networking & Storage
  • docker-compose for Local Development
  • Health Checks & Production Best Practices

Kubernetes Essentials

  • Core Objects
  • Services & Ingress
  • Config & Secrets
  • Probes & Autoscaling
  • Deployments & Rollouts

AWS Cloud Fundamentals

  • EC2
  • S3
  • RDS
  • IAM
  • VPC Basics

DevOps Practices

  • CI/CD Pipeline Design
  • Deployment Strategies
  • Infrastructure as Code Awareness
  • Monitoring in Production
  • Cost Awareness
HomeLearnDevOpsCloud & DevOps FundamentalsDocker Fundamentals
βœ“ FreeBeginnerΒ· 8 min read

Docker Fundamentals

Images vs containers, the core Dockerfile instructions, why layer ordering determines build speed, and the 'latest' tag trap that causes real production incidents.

Published September 23, 2026


Docker Fundamentals

Images vs containers β€” the distinction that everything else builds on

An image is an immutable, read-only template β€” a filesystem snapshot plus metadata (what command to run, what ports to expose). A container is a running (or stopped) instance created FROM an image, with its own writable layer on top. This is directly analogous to a class vs an object: you can start many containers from the same image, each an independent running instance, exactly as you'd instantiate many objects from one class. Confusing the two is a common source of early Docker mistakes β€” deleting "a container" doesn't touch the image it came from, and rebuilding an image doesn't affect containers already running from the old version.

Advertisement

The core Dockerfile instructions

FROM eclipse-temurin:21-jre-alpine   # base image β€” everything starts from here
WORKDIR /app                          # sets the working directory for subsequent instructions
COPY target/app.jar app.jar           # copies a file from the build context into the image
EXPOSE 8080                           # documents the port the app listens on (doesn't publish it)
ENTRYPOINT ["java", "-jar", "app.jar"] # the command that always runs when the container starts
  • FROM: the base image every subsequent instruction builds on top of β€” almost always the first line.
  • COPY: copies files from the build context (your local directory) into the image.
  • RUN: executes a command AT BUILD TIME, and its result (e.g. installed packages) becomes part of the image layer.
  • WORKDIR: sets the directory subsequent instructions operate relative to, avoiding repeated absolute paths.
  • EXPOSE: purely documentation β€” it does NOT actually publish the port to the host (that's the -p flag at docker run time, covered in Networking & Storage).

ENTRYPOINT vs CMD

ENTRYPOINT ["java", "-jar", "app.jar"]   # the fixed command β€” always runs
CMD ["--spring.profiles.active=default"]  # default ARGUMENTS to that command β€” overridable at `docker run`

This distinction trips up nearly everyone at first: ENTRYPOINT defines the command that always executes; CMD (when both are present) supplies DEFAULT arguments to that entrypoint, which can be overridden by whatever's passed at the end of docker run <image> <override-args>. Used alone, CMD defines the entire default command instead. The practical pattern above β€” a fixed ENTRYPOINT plus an overridable CMD for default args β€” is what lets the same image be run with different profiles/flags without rebuilding it.

Layer caching: why instruction order matters for build speed

# SLOW β€” any source code change invalidates the dependency-install layer too
COPY . .
RUN mvn install

# FAST β€” dependencies only reinstall when pom.xml actually changes
COPY pom.xml .
RUN mvn dependency:go-offline    # downloads dependencies, cached as its own layer
COPY src ./src
RUN mvn install                  # only THIS layer rebuilds on a normal source change

Docker builds an image as a stack of layers, and CACHES each layer β€” but the moment ONE instruction's input changes, every layer AFTER it must rebuild, even if their own inputs didn't change. This is why the standard pattern copies rarely-changing files (dependency manifests) and installs dependencies BEFORE copying frequently-changing source code β€” a source-only change then only invalidates the small, fast final layers, not the expensive dependency-download step.

The union filesystem

Each Dockerfile instruction that modifies the filesystem (COPY, RUN) creates a new, immutable layer; the final image is these layers stacked and presented as one unified filesystem view via a union filesystem β€” a file present in an early layer and later deleted in a subsequent layer still physically exists in the early layer (contributing to image size) even though it's invisible in the final merged view. This is why simply RUN rm -rf /some/large/file in a LATER instruction doesn't actually shrink the image β€” the file still exists in the earlier layer; it needs to be removed in the SAME RUN instruction that created it to avoid it ever becoming part of a separate layer.

The 'latest' tag trap

FROM myapp:latest   # DANGEROUS in production β€” which actual version is this, right now?
FROM myapp:1.4.2    # explicit, reproducible, safe

latest isn't a special, automatically-updating tag β€” it's just the default tag Docker applies when no tag is specified, and it points to whatever image was most recently pushed WITHOUT an explicit tag. Relying on latest in a production deployment manifest means the exact version actually running is ambiguous and can silently change between deployments of DIFFERENT services that happen to reference the same tag β€” a real, avoidable source of "it worked yesterday" incidents. Explicit, immutable version tags (or a content-addressable digest) are the standard production practice.

Follow-up questions this topic invites β€” and their answers

Q: If ENTRYPOINT and CMD are both arrays, why does the exact syntax (array vs plain string) matter? A: The array form (["java", "-jar", "app.jar"], called 'exec form') runs the command directly as PID 1 without a shell wrapping it; the plain-string form runs it through /bin/sh -c, which adds a shell process and can interfere with signal handling (like SIGTERM for graceful shutdown, discussed in production contexts) β€” exec form is the generally-preferred, more predictable choice for production images.

Q: Does layer caching work the same way across different machines, or only locally? A: Locally, Docker's build cache is machine-specific by default; in CI/CD pipelines, this cache often needs to be EXPLICITLY persisted/restored between builds (via a registry-based cache or a build-cache volume) or every CI run rebuilds from scratch with no cache benefit at all β€” a common, easy-to-miss optimization gap in a naive CI setup.

Q: Why alpine or slim base images specifically, beyond just smaller download size? A: A smaller base image also means a smaller attack surface (fewer installed packages that could carry vulnerabilities) and faster container startup β€” directly relevant to Health Checks & Production Best Practices' vulnerability-scanning and minimal-base-image discussion later in this chapter.

Q: What actually happens if you never specify WORKDIR? A: Instructions default to running relative to the image's root filesystem context set by the base image (often /) β€” WORKDIR isn't strictly required, but omitting it means every COPY/RUN needs a full path, which is both more error-prone and less readable than setting it once.

Advertisement

Next

Multi-Stage Builds

AI Tutor

Lesson: Docker Fundamentals

Quick actions

AI responses can be inaccurate. Verify critical information.