Back to blog

I learnt Docker so you don't have to

An Introduction to Containers and Environment Consistency

Divine Amunega Divine Amunega
4 min read
I learnt Docker so you don't have to

I lied.

I didn’t learn Docker so you don’t have to.
I learned it because it’s genuinely fun and incredibly useful if you’re serious about a career in software engineering.

So let’s get into it. What is Docker.. why does everyone keep saying “containers” when talking about it? What is an image? Can Docker take pictures? What is the importance of Docker? Why does my code break on prod when i run git push? All these and more I’ll answer in this here.

What is Docker

Docker is an open-source containerization platform that allows developers to package applications and all their dependencies into a standardized, isolated unit called a container. This ensures the application runs consistently and reliably across any computing environment, from a developer's laptop to a testing server or a production cloud platform, effectively solving the "it works on my machine" problem.

When developers build applications, they rarely start from scratch. Instead, they rely on existing code, packages, and tools to move faster.

For example, if I were building an e-commerce site, I’d probably use:

  • a Node.js runtime (because I’m more comfortable with TypeScript),

  • Sanity as a CMS,

  • Clerk for authentication,

  • and other libraries depending on the features I need.

Before that application can even run, all of these pieces must be present and correctly configured. If any one of them is missing the runtime, a package, an environment variable the application simply won’t work.

All the packages, tools, and software that an application needs in order to run are called dependencies.

This poses a big problem during hosting.

Your local machine already has a lot of things installed: a specific Node.js version, system libraries, environment variables, and cached dependencies. The server you deploy to does not. It starts clean.

So when you push your code to production, you’re not just deploying your application, you’re implicitly assuming that the server has the same environment you were developing in.

Most of the time, that assumption is wrong.

That’s why:

  • your app works locally but crashes in production,

  • a dependency behaves differently on the server,

  • a package installs fine on your machine but fails in CI,

  • or a minor OS difference suddenly becomes a runtime bug.

This isn’t a coding problem.
It’s an environment problem.

And historically, developers tried to solve it with:

  • long setup docs,

  • shell scripts,

  • manual server configuration,

  • or “just install Node v20 and hope for the best”.

These approaches don’t scale, they drift over time, and they break silently.

This is the exact problem Docker was built to solve.

Docker was built to let developers define their runtime environment as code and ship it alongside the application, so the same environment can be reproduced across development, CI, staging, and production.

Images & Containers

A Docker image is a read-only template that contains everything needed to run an application: the filesystem, runtime, libraries, and the instructions for how the application should start.

A Docker container is what you get when that image is executed. It’s the running form of the image, isolated from the rest of the system, but using the exact environment defined inside the image.

You can start multiple containers from the same image, each behaving independently, even though they all originate from the same definition.

Simply put, think of it like this: the Docker image is the recipe, and the Docker container is the cooked food. The recipe tells you exactly how the meal should be made. The container is the meal while it’s being eaten.

How Images Are Created

Docker images are built using the Docker CLI and a file called a Dockerfile.

A Dockerfile is a plain text file that contains a sequence of instructions describing what the image should look like: which base image to start from, which dependencies to install, which files to copy into the image, and how the application should be started.

When an image is built, everything defined in the Dockerfile becomes part of that image. From that point on, the environment described by the image does not change. If you need to update a dependency, modify the filesystem, or alter how the application starts, you don’t change the image directly. You update the Dockerfile and build a new image.

That’s why it’s called an image.

This immutability is intentional. It’s what makes Docker environments predictable and reproducible.

The Core Idea

So the problem Docker solves is simple.

Docker brings consistency to development workflows.

When the environment stops changing, an entire class of bugs disappears.

This is the first part of a multi-part series on Docker. In the next parts, I’ll build on this foundation and get into how Docker is used in practice.