How to Build Custom Docker Images with Dockerfile

While pre-built images cover many needs, building your own Docker image lets you package your specific application exactly as needed. This guide covers writing an effective Dockerfile.

What a Dockerfile Does

A text file containing instructions for building a Docker image — starting from a base image, then adding your application code, dependencies, and configuration in defined layers.

A Basic Dockerfile Example (Node.js Application)

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --production

COPY . .

EXPOSE 3000
CMD ["node", "server.js"]

Understanding Key Instructions

InstructionPurpose
FROMThe base image to build upon
WORKDIRSets the working directory for subsequent instructions
COPYCopies files from your build context into the image
RUNExecutes a command during image build (installing dependencies, etc.)
EXPOSEDocuments which port the container listens on (informational, doesn't actually publish it)
CMDThe default command run when a container starts from this image

Building the Image

docker build -t myapp:1.0 .

. specifies the build context (current directory) — Docker sends this entire directory to the build process, so keep it focused on what's actually needed.

Choosing a Good Base Image

Prefer smaller, purpose-built base images (like Alpine variants) over full general-purpose OS images when compatible — smaller images build faster, transfer faster, and have less attack surface.

Optimizing Layer Caching

COPY package*.json ./
RUN npm ci --production
COPY . .

Copying dependency manifests and installing dependencies before copying the rest of your code means Docker can reuse the cached dependency-install layer on subsequent builds, as long as dependencies haven't changed — significantly speeding up iterative builds.

Using .dockerignore

node_modules
.git
.env
*.log

Prevents unnecessary files from being included in the build context, keeping builds faster and avoiding accidentally including sensitive files (like .env) in your image.

Setting a Non-Root User

RUN addgroup -g 1001 appgroup && adduser -D -u 1001 -G appgroup appuser
USER appuser

See How to Run Docker Containers as a Non-Root User — a meaningful security improvement over the default root user.

Tagging Images Meaningfully

docker build -t myapp:1.2.0 -t myapp:latest .

Use specific version tags, not just latest, for genuine traceability of what's actually deployed — latest alone makes it hard to know exactly which code version is running.

Testing Your Built Image

docker run --rm -p 3000:3000 myapp:1.0

Common Errors

Build succeeds but the application fails to start — verify the CMD instruction correctly matches how your application should actually be started, and check for missing runtime dependencies not included in the build.

Build is very slow — review your Dockerfile's instruction order for caching efficiency, and check your .dockerignore is excluding unnecessary large files/directories from the build context.

Continue Reading

Browse more articles in Docker & Containers.

  • dockerfile tutorial, build custom docker image, docker build command, dockerfile best practices
  • 0 Корисниците го најдоа ова како корисно
Дали Ви помогна овој одговор?

Понудени резултати

How to Install Docker Engine on Ubuntu & Debian (Complete Guide)

Docker lets you package applications and their dependencies into lightweight, isolated containers...

How to Install Docker Compose on Ubuntu & Debian

Docker Compose lets you define and run multi-container applications from a single YAML file,...

Deploy Your First Docker Container: A Beginner's Walkthrough

With Docker installed, this hands-on walkthrough covers the core workflow you'll use constantly:...

How to Update Docker and Docker Compose Safely

Keeping Docker Engine and Compose updated brings security fixes, performance improvements, and...

Docker Compose Troubleshooting: Common Errors & Fixes

A reference guide to the Docker and Docker Compose errors you're most likely to encounter, with...