Published: 2026-06-01 β€’ Updated: 2026-07-05

Writing Your First Dockerfile: Complete Real-World Guide for Beginners, Developers, and DevOps Engineers

A Dockerfile is one of the most important concepts in Docker and containerization. Almost every real-world Docker project starts with writing a Dockerfile. Whether you are building a Spring Boot application, React frontend, Node.js API, Python service, or microservices platform, Dockerfiles are used to create consistent and portable application environments.

Many beginners initially think Dockerfiles are just simple configuration files. In reality, Dockerfiles directly affect:

  • Application deployment speed
  • CI/CD pipeline performance
  • Docker image size
  • Cloud infrastructure cost
  • Application startup time
  • Kubernetes scaling speed
  • Production reliability
  • Security and maintainability

In real production systems, poorly written Dockerfiles can create very large images, slow down deployments, increase cloud costs, and even expose security vulnerabilities.

That is why experienced DevOps engineers and backend developers pay close attention to Dockerfile optimization.

Before learning Dockerfiles deeply, it is recommended to understand Docker Installation Docker Architecture Docker CLI Commands Docker Images and Layers Dockerfile Tutorial Docker Compose Guide Spring Boot Microservices Kubernetes Introduction

What is a Dockerfile?

A Dockerfile is a text file that contains instructions used to build a Docker image.

Think of it like a recipe.

Just as a cooking recipe tells how to prepare a dish step by step, a Dockerfile tells Docker how to build an application environment step by step.

Simple Workflow

[ Dockerfile ]
       |
       v
docker build
       |
       v
[ Docker Image ]
       |
       v
docker run
       |
       v
[ Running Container ]

In simple terms:

  1. Write Dockerfile
  2. Build image using Dockerfile
  3. Run containers from image

Why Dockerfile is Important?

Before Docker became popular, developers manually installed software on servers:

  • Install Java
  • Install dependencies
  • Configure environment variables
  • Copy application files
  • Start application manually

This process caused many real-world problems:

  • Applications worked on one machine but failed on another
  • Different developers used different environments
  • Deployment steps were inconsistent
  • Server setup became difficult
  • Scaling applications was slow

Dockerfiles solve these problems by defining infrastructure and application setup in a repeatable way.

Realistic Example

Suppose a company has 20 developers working on the same Spring Boot microservices platform.

Without Dockerfiles:

  • Some developers may use Java 17
  • Others may use Java 21
  • Dependencies may differ
  • Application behavior becomes inconsistent

With Dockerfiles:

  • Everyone uses the same environment
  • Same Java version
  • Same dependencies
  • Same startup configuration

This consistency is one of the biggest reasons Docker became extremely popular in modern software engineering.

Basic Dockerfile Structure

A Dockerfile usually contains instructions like:

  • FROM β†’ Base image
  • WORKDIR β†’ Working directory
  • COPY β†’ Copy files
  • RUN β†’ Execute commands
  • CMD β†’ Start application

Basic Example

FROM openjdk:17

WORKDIR /app

COPY target/app.jar app.jar

CMD ["java", "-jar", "app.jar"]

Understanding Each Dockerfile Instruction

1. FROM Instruction

FROM openjdk:17

This defines the base image.

In this example, the container already includes Java 17 runtime.

Realistic Example

Suppose a company develops multiple Spring Boot applications. Instead of manually installing Java on every server, the Docker image already contains Java runtime.

This ensures:

  • Correct Java version
  • Consistent environment
  • Portable deployment

2. WORKDIR Instruction

WORKDIR /app

Sets the working directory inside the container.

All future commands execute relative to this folder.

Why WORKDIR is Important?

Without WORKDIR, files may be copied into random locations inside the container, making debugging difficult.

Realistic Example

In production debugging, developers may enter containers using:

docker exec -it container_name /bin/bash

A clean folder structure makes troubleshooting easier.

3. COPY Instruction

COPY target/app.jar app.jar

Copies files from local system into container image.

Realistic Example

Suppose Jenkins builds a Spring Boot JAR file during CI/CD pipeline:

target/app.jar

The COPY instruction moves this file into the Docker image.

4. RUN Instruction

RUN apt-get update

Executes commands during image build.

Common uses:

  • Install packages
  • Create directories
  • Download dependencies
  • Configure environment

Realistic Example

Suppose a Python application needs image-processing libraries:

RUN apt-get install -y imagemagick

Docker installs the dependency during image creation.

5. CMD Instruction

CMD ["java", "-jar", "app.jar"]

Defines the default startup command for the container.

Realistic Example

When the container starts, Docker automatically runs the Spring Boot application using this command.

Complete Real-World Spring Boot Example

FROM eclipse-temurin:17-jdk-jammy

WORKDIR /app

COPY target/payment-service.jar payment-service.jar

EXPOSE 8080

CMD ["java", "-jar", "payment-service.jar"]

Explanation

  • Uses Java 17 runtime
  • Creates clean application folder
  • Copies Spring Boot application
  • Documents application port
  • Starts application automatically

Build Image

docker build -t payment-service .

Run Container

docker run -d -p 8080:8080 payment-service

Open browser:

http://localhost:8080

Dockerfile Build Process Internally

Many beginners use Dockerfiles without understanding what happens internally.

Internal Build Flow

Read Dockerfile
       |
       v
Process Instructions One by One
       |
       v
Create Layers
       |
       v
Store Layers in Cache
       |
       v
Generate Final Docker Image

Every Dockerfile instruction creates a separate image layer.

Docker Layers and Caching

One of Docker’s biggest advantages is layer caching.

Suppose your Dockerfile looks like:

FROM openjdk:17

WORKDIR /app

COPY pom.xml .

RUN mvn dependency:resolve

COPY src ./src

RUN mvn clean package

Realistic Example

If only source code changes:

  • Docker reuses dependency layers
  • Only rebuilds application code layer

This significantly improves CI/CD build speed.

In large organizations with hundreds of daily deployments, optimized caching can save hours of build time.

Realistic Example: Microservices Architecture

In modern microservices architecture, every service usually has its own Dockerfile.

[ API Gateway ]
      |
      +----------------------+
      |                      |
      v                      v
[ User Service ]     [ Payment Service ]
      |                      |
      v                      v
[ MySQL ]             [ Redis ]

Each service:

  • Has separate Dockerfile
  • Builds independent image
  • Deploys independently
  • Scales independently

This is why Dockerfiles are foundational for Docker Compose Guide Spring Boot Microservices Kubernetes Introduction

Realistic Example: CI/CD Pipeline

Dockerfiles are heavily used in CI/CD automation.

Developer Pushes Code
        |
        v
CI/CD Pipeline Starts
        |
        v
Build Application
        |
        v
Read Dockerfile
        |
        v
Build Docker Image
        |
        v
Push Image to Docker Registry
        |
        v
Deploy Container to Server

Real-world tools:

  • Jenkins
  • GitHub Actions
  • GitLab CI/CD
  • Azure DevOps

Dockerfiles make deployments repeatable and automated.

Realistic Example: Frontend React Application

Dockerfiles are not only for backend applications.

Example React Dockerfile:

FROM node:20

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

RUN npm run build

CMD ["npm", "start"]

Realistic use case:

Frontend teams use Dockerfiles to ensure all developers use the same Node.js version and dependencies.

Understanding EXPOSE Instruction

EXPOSE 8080

Documents which port the application uses.

Important:

EXPOSE does not automatically publish ports externally.

External mapping still requires:

docker run -p 8080:8080 app-name

CMD vs ENTRYPOINT

CMD

Provides default startup command.

Can be overridden easily.

ENTRYPOINT

Defines fixed executable.

Harder to override.

Realistic Example

ENTRYPOINT is often used in production containers where the application startup command should remain fixed.

What is Build Context?

docker build -t app-name .

The dot (.) represents build context.

Docker sends all files from this folder to Docker Daemon during build.

Realistic Problem

Many beginners accidentally copy:

  • node_modules
  • .git folder
  • logs
  • temporary files

This increases image size significantly.

Using .dockerignore

.dockerignore prevents unnecessary files from entering build context.

Example

node_modules
.git
logs
target

Real-world benefit:

  • Smaller images
  • Faster builds
  • Reduced network transfer

Realistic Example: Large Image Problem

Many beginner Dockerfiles accidentally create 2GB+ images.

Common reasons:

  • Using full Ubuntu base image
  • Installing unnecessary packages
  • Copying complete project folders
  • Not cleaning temporary files

Large images create major production problems:

  • Slow CI/CD pipelines
  • Slow deployments
  • Higher cloud storage cost
  • Slow Kubernetes scaling

Realistic Example: Kubernetes Scaling

Suppose an e-commerce platform suddenly receives heavy traffic during a sale.

Kubernetes may need to create:

100+ new containers

If image size is:

  • 150MB β†’ Fast scaling
  • 2GB β†’ Slow scaling

Optimized Dockerfiles improve scaling speed dramatically.

Multi-Stage Builds

Multi-stage builds are used to reduce image size.

Realistic Example

FROM maven:3.9.6-eclipse-temurin-17 AS build

WORKDIR /app

COPY . .

RUN mvn clean package -DskipTests

FROM eclipse-temurin:17-jdk-jammy

WORKDIR /app

COPY --from=build /app/target/app.jar app.jar

CMD ["java", "-jar", "app.jar"]

Why This is Better?

  • Maven dependencies stay in build stage only
  • Final image becomes smaller
  • Production deployment becomes faster

This pattern is extremely common in real enterprise applications.

Security Best Practices

1. Avoid Running as Root

Containers running as root increase security risk.

2. Use Trusted Base Images

Prefer official images from trusted registries.

3. Avoid Storing Secrets

Never store:

  • Database passwords
  • API keys
  • JWT secrets

directly inside Dockerfiles.

4. Keep Images Updated

Old images may contain security vulnerabilities.

Common Dockerfile Mistakes

1. Wrong Instruction Order

Frequently changing instructions should appear near bottom for better caching.

2. Large Build Context

Copying unnecessary files increases image size.

3. Using Large Base Images

Large images slow deployments and scaling.

4. Ignoring Layer Optimization

Poor layer structure slows CI/CD builds.

5. Modifying Running Containers Manually

Production changes should happen through Dockerfile rebuilds, not manual container modifications.

Interview Questions

What is Dockerfile?

A text file containing instructions used to build Docker images.

What is build context?

Files sent to Docker Daemon during build process.

Difference between CMD and ENTRYPOINT?

CMD provides default command. ENTRYPOINT defines fixed executable.

Why layers are important?

Layers improve caching, storage efficiency, and build performance.

What is multi-stage build?

Technique used to reduce final image size by separating build and runtime stages.

Interview Trap Questions

Does EXPOSE publish ports automatically?

No. EXPOSE only documents ports.

If source code changes, will all layers rebuild?

No. Docker rebuilds only affected layers.

Can Dockerfile create multiple images?

One Dockerfile usually creates one final image, though multi-stage builds use temporary intermediate stages.

Should secrets be stored in Dockerfile?

No. Secrets should use environment variables or secret managers.

Recommended Learning Path

  1. Docker Installation
  2. Docker Architecture
  3. Docker CLI Commands
  4. Docker Images and Layers
  5. Dockerfile Tutorial
  6. Docker Compose Guide
  7. Spring Boot Microservices
  8. Kubernetes Introduction

Conclusion

Dockerfiles are the foundation of modern containerized applications. They allow developers to define application environments in a repeatable, portable, and automated way.

Well-designed Dockerfiles improve build performance, reduce image size, speed up deployments, optimize CI/CD pipelines, and make applications easier to scale.

In real-world software engineering, Dockerfiles are used daily in backend development, microservices architecture, DevOps automation, Kubernetes deployments, and cloud-native systems.

After mastering Dockerfiles, continue learning Docker Compose Guide Spring Boot Microservices Kubernetes Introduction

About the Author

Naresh Kumar

Naresh Kumar

Senior Java Backend Engineer experienced in Banking, Payments, ISO 20022, Spring Boot, Microservices, Kafka, Docker, Kubernetes, AWS and Cloud Native Systems.

Built enterprise payment solutions, transaction processing systems, API platforms and scalable microservices used in production.

LinkedIn Profile