Introduction
Knowing how to run containers is only half the story - sooner or later, you'll need to package your own applications into container images. This skill path teaches you Dockerfile authoring from the ground up, starting with the simplest possible image and building up to production-grade multi-stage builds. You'll learn how to:
- Build and publish a container image to a registry
- Write Dockerfiles using core instructions:
FROM,COPY,RUN, andCMD - Handle application and system-level dependencies in a Dockerfile
- Compile and build applications inside a Dockerfile
- Inspect container image internals (layers, sizes, digests)
- Optimize images with multi-stage builds to produce smaller, cleaner production artifacts
By the end of this skill path, you'll be comfortable writing Dockerfiles for real-world applications and understand how to keep your images lean and efficient.

Prerequisites
- Linux command-line knowledge
- Familiarity with installing OS packages and tar archiving/unarchiving
- Basic container running skills (see Docker 101: Run and Manage Containers)
Build and Publish a Container Image
Before diving into Dockerfile authoring, let's get familiar with the core workflow:
building an image from a Dockerfile and pushing it to a container registry.
This challenge walks you through the entire process using a pre-written Dockerfile -
so you can focus on the docker build and docker push commands and the image naming convention.

Write Your First Dockerfile
Now it's time to write your own Dockerfile from scratch.
Every container image starts with a base image (FROM), some files to copy in (COPY),
and a default command to run (CMD) - that's really all you need to containerize a simple application.

In this challenge, you'll containerize a minimal Node.js server using just three Dockerfile instructions:
Write a Dockerfile for a Web Server Application
Most real-world applications aren't single-file scripts - they come with dependencies that need to be installed during the image build.
This is where the RUN instruction comes in, allowing you to execute arbitrary commands (like npm install) as part of the build process.
In this challenge, you'll write a Dockerfile for an Express.js web server that has external npm dependencies:
Exclude Dev Dependencies from the Production Image
Development tools like linters, test frameworks, and hot-reload utilities have no place in a production container image - they bloat the image size and expand the attack surface. Most package managers provide a way to install only production dependencies, and knowing how to use it in a Dockerfile is a practical skill worth acquiring early.
In this challenge, you'll write a Dockerfile that keeps the production image lean by excluding all development dependencies:
Handle System-Level Dependencies in a Dockerfile
Some application libraries depend on system-level packages (C libraries, shared objects, etc.)
that aren't included in the base image.
When this happens, you need to install OS-level packages with apt-get or similar tools
before installing the application's own dependencies.
In this challenge, you'll write a Dockerfile for a Python application that requires both system-level and Python-level packages:
Build and Compile Applications in Dockerfiles
So far, the applications you've containerized were interpreted (Node.js, Python) and didn't require a compilation step. But many production applications are written in compiled languages like Go, Rust, Java, or TypeScript, and their source code must be built into a binary or a bundle before it can run.

In this challenge, you'll compile both a Go backend and a TypeScript frontend inside their Dockerfiles:
Build and Inspect a Container Image
Understanding what's inside a container image - its layers, sizes, and contents -
is essential for troubleshooting build issues and optimizing image size.
Tools like docker image inspect and dive let you peek under the hood
and see exactly how your Dockerfile instructions translate into image layers.
In this challenge, you'll build an image, then use inspection tools to answer questions about its internal structure:
How to Build Smaller Container Images
Loading tutorial...
If you completed the previous challenge, you may have noticed that single-stage Dockerfiles for compiled applications produce unnecessarily large images - the final image includes the compiler, build tools, and source code that are no longer needed at runtime.
Docker's multi-stage builds solve this problem by allowing you to use multiple FROM instructions in a single Dockerfile.
This tutorial explains the technique in depth, with practical examples for Node.js, Go, Python, Rust, and other stacks.
Optimize Container Images with Multi-Stage Builds
Time to put the theory into practice! Remember the Go backend and TypeScript frontend you containerized earlier using single-stage Dockerfiles? Those images included compilers, build tools, and dev dependencies that bloat the final artifact.

In the capstone challenge of this skill path, you'll rewrite both Dockerfiles using multi-stage builds to produce smaller, production-ready images: