You’ve built something — a little web app, an API, a script — and it runs beautifully on your machine. Now you want it to run anywhere: on a teammate’s laptop, a test server, the cloud, all behaving identically. “Dockerizing” it is how you do that, and it’s far less intimidating than it looks. At its heart, it’s writing one short text file.
This is a beginner’s guide to how to dockerize an app — conceptually, step by step, with the real building blocks. We’ll keep it to the shape of the process (no copy-paste-and-pray), so that when you write your own Dockerfile for your own project, you understand every line.

What “Dockerizing” Actually Means
Dockerizing an app means packaging it — along with everything it needs to run (its runtime, libraries, and files) — into a container image. Recall the key distinction: an image is the blueprint you build, and a container is a running instance of it. So dockerizing = writing the recipe that builds the blueprint.
That recipe is a single plain-text file called a Dockerfile. Docker reads it top to bottom and assembles your image. Once you have the image, anyone can run it as a container and get the exact same environment you had — which is how “but it works on my machine!” finally dies.
The Building Blocks: Dockerfile Instructions
A Dockerfile is a list of instructions, each a keyword followed by its detail. A valid Dockerfile must start with FROM. Here are the ones you’ll actually use, at a beginner level:

| Instruction | What it does |
|---|---|
FROM | Sets the base image to build on (and must come first) |
WORKDIR | Sets the working directory inside the image for later steps |
COPY | Copies files from your project into the image |
RUN | Runs a command at build time — e.g. installing dependencies |
ENV | Sets an environment variable that persists at runtime |
EXPOSE | Documents which port the app listens on |
CMD | The default command run when a container starts |
ENTRYPOINT | The main executable the container runs |
Two of these routinely trip up beginners, so let’s be precise:
RUNvs.CMD.RUNexecutes while the image is being built (like installing packages — it becomes part of the image).CMDruns when the finished container starts (like launching your web server). Build-time vs. run-time.EXPOSEdoesn’t actually open a port. This surprises everyone.EXPOSEis documentation — it tells humans and tools “this app listens on port 3000.” To actually make the port reachable, you publish it when you run the container (with the-pflag).EXPOSElabels;-pconnects.
A Conceptual Walkthrough
Here’s the shape of a typical Dockerfile for a simple web app. Read it as a story, not a snippet to memorize:

FROMa base image. Start from an official image for your language (say, a Python or Node runtime), so you don’t have to build the operating system and interpreter yourself.WORKDIRa folder. Set a working directory inside the image (like/app) so subsequent steps have a home.COPYthe dependency list, thenRUNthe install. Copy in just your dependency manifest (likerequirements.txtorpackage.json) and run the install command. Do this before copying your code — and the next section explains why that order matters.COPYthe rest of your app. Now copy your actual source code into the image.EXPOSEthe port. Note the port your web server listens on (documentation for humans and tools).CMDthe start command. Specify the command that launches your app when the container runs.
Build that, and out comes an image. Run the image, and you have a container serving your app — identical for everyone.
The One Pro Tip That Makes Builds Fast: Layer Order
Here’s the detail that separates a frustrating Docker experience from a smooth one. Each Dockerfile instruction creates a layer, and Docker caches those layers. On a rebuild, unchanged layers are reused instead of redone — so builds are fast. But there’s a catch: changing one layer invalidates every layer after it.

That’s why step 3 above copies dependencies and installs them before copying your source code. Your dependencies change rarely; your code changes constantly. If you install dependencies first, then a one-line code edit only rebuilds the quick final layers — the slow dependency install stays cached. Flip the order (copy all your code first, then install), and every tiny code change forces a full, slow reinstall. Same instructions, very different experience. Order your Dockerfile from least-changing to most-changing, and your builds stay snappy.
A Couple of Good Habits
Two quick things that save real pain:
- Use a
.dockerignorefile. Just like.gitignore, it keeps junk (local logs,node_modules,.git, secrets) out of your image — smaller, cleaner, and safer builds. - Never bake secrets into the image. Don’t
COPYa file full of passwords or API keys into the image — image layers are readable, and anyone with the image can extract them. Pass secrets in at runtime instead (via environment variables or a secrets manager).
Dockerizing an AI Model Works the Same Way
Here’s the satisfying part: this exact pattern is how AI models get shipped. A trained model plus its Python environment and serving code is dockerized with the same recipe — FROM a base image, COPY in the model and code, RUN to install libraries, CMD to start the server. It kills the “works in my notebook” problem the same way it kills “works on my machine.” It’s also, essentially, what happens under the hood when you run an AI model locally with Docker. Learn to dockerize a web app and you’ve learned to dockerize almost anything — models included.
❓ Frequently Asked Questions
What does it mean to “dockerize” an app? Dockerizing an app means packaging it, along with everything it needs to run (runtime, libraries, files), into a container image using a Dockerfile. The result is a portable image that runs identically on any machine with a container runtime — eliminating “works on my machine” problems.
What is a Dockerfile? A Dockerfile is a plain-text file containing step-by-step instructions Docker uses to build an image. It always starts with a FROM instruction (the base image), then adds steps like copying your code, installing dependencies, and setting the command to run when the container starts.
What’s the difference between RUN and CMD in a Dockerfile? RUN executes a command while the image is being built (for example, installing dependencies), and its result becomes part of the image. CMD specifies the command that runs when a container starts from the image (for example, launching your app). Build-time versus run-time.
Why should I install dependencies before copying my code? Because Docker caches each build step as a layer, and changing an early step invalidates all later ones. Dependencies change rarely and your code changes often, so installing dependencies first keeps that slow step cached — a small code edit then rebuilds only the fast final layers instead of reinstalling everything.
Does EXPOSE make my app reachable? No — EXPOSE only documents which port your app listens on; it doesn’t actually open it. To make the port reachable from your machine, you publish it when you run the container using the -p flag (for example, mapping a host port to the container’s port).
📚 Keep Learning
- Docker Images vs. Containers — the blueprint-vs-instance idea a Dockerfile builds on.
- Run AI Models Locally With Docker — the same skills, applied to AI.
- Container Registries Explained — where to push your freshly-built image.
📚 Recommended Resources
Disclosure: some links below are Amazon affiliate links. If you buy through them, I earn a small commission — at no extra cost to you — which helps keep this site running.
Docker Deep Dive
Nigel Poulton · Book · ~$30 on Amazon
The perfect next step after your first Dockerfile — the standard, best-selling Docker book, clear and hands-on. It takes the concepts here and turns them into real, confident skills with images, containers, volumes, and networking.
The DevOps Handbook (2nd Edition)
Gene Kim, Jez Humble, Patrick Debois & John Willis · Book · ~$25 on Amazon
Once you can dockerize an app, this is the guide to the practices that put that skill to work — automated build-and-deploy pipelines, continuous delivery, and the culture that ships software fast and safely.