Introduction
Containerization is the process of packaging an application and its dependencies into an image and running it as a container. The overall workflow follows five steps: write the app and list its dependencies, create a Dockerfile, build the image, optionally push it to a registry, and run a container from it.
Getting the Application Code
The example used here is a simple Node.js web app. After cloning it locally, the application directory becomes the build context — the set of files Docker uses when building the image.
git clone https://github.com/nigelpoulton/ddd-book.gitCreating the Dockerfile
Newer versions of Docker support the docker init command, which analyzes the application and automatically generates a Dockerfile following good practices:
docker initThe generated Dockerfile typically includes several key instructions: a FROM line pulling a base image, a WORKDIR setting the working directory, a RUN instruction installing dependencies, a USER instruction to avoid running as root, a COPY instruction to add source code, an EXPOSE instruction documenting the network port, and a CMD instruction specifying how to start the app.
Building the Image
Once the Dockerfile is ready, the following command builds it into an image, using the current directory as the build context:
docker build -t ddd-book:ch8.node .Each Dockerfile instruction that adds content — such as FROM, RUN, or COPY — produces a new image layer. The resulting image can be confirmed with:
docker imagesRunning docker inspect on the image reveals its layers along with metadata like exposed ports and working directory.
Pushing the Image to Docker Hub
To share an image, it first needs to be logged in and re-tagged to include the target repository, since Docker uses the tag to determine where to push it:
docker login
docker tag ddd-book:ch8.node nigelpoulton/ddd-book:ch8.node
docker push nigelpoulton/ddd-book:ch8.nodeRunning and Testing the App
A container can be started from the pushed (or locally built) image with:
docker run -d --name c1 -p 5005:8080 nigelpoulton/ddd-book:ch8.nodeThe app becomes reachable in a browser through the mapped host port, and docker ps confirms the container is running with the correct port mapping.
Looking a Bit Closer
The docker build process reads the Dockerfile top to bottom. Instructions that add content — FROM, RUN, COPY, WORKDIR — create new layers, while instructions like EXPOSE, ENV, and CMD only add metadata. The docker history command shows exactly which instructions produced layers and their sizes:
docker history ddd-book:ch8.nodeIt's generally good practice to base new images on Docker Official Images or Verified Publisher images, since they are actively maintained and quickly patched for known vulnerabilities.
Conclusion
Containerizing an application comes down to a repeatable pattern: define the build in a Dockerfile, build it into an image, optionally share it via a registry, and run it as a container. Understanding which Dockerfile instructions create layers versus metadata helps in writing cleaner, more efficient images going forward.