Table of Contents
From Local App to Dockerized Web Service
In this chapter you will walk through the process of taking a very simple web application that runs on your local machine and turning it into a Dockerized service. The goal is to understand the practical steps from “I have some code” to “I can run it in a container on any machine with Docker installed.”
We will not focus on any specific programming language framework in depth, because the ideas are the same for a small application whether it is written in Node.js, Python, or another common web technology.
Defining the Minimal Web Application
To Dockerize a simple web app, you first need a minimal application that listens on a TCP port and responds to HTTP requests. For example, this might be a small HTTP server that listens on port 8000 and returns “Hello, Docker” when you visit it in a browser.
Before you think about containers, always confirm that your app runs correctly on your development machine without Docker. If your app requires a runtime such as Node.js or Python, or relies on package managers such as npm or pip, make sure you can install dependencies and start the server with a single command like npm start or python app.py.
Always verify that your web app runs correctly on your host before Dockerizing it. Docker will not fix a broken application.
Once you can start the server and see a response in the browser, you are ready to capture this environment and behavior in a container image.
Choosing a Base Image
The Dockerization process starts in your Dockerfile with the choice of a base image. For a simple web app this is usually an official language runtime image. For instance, if your app is in Node.js you might choose node:20-alpine. If it is in Python you might choose python:3.12-alpine.
Your choice affects image size, available tools, and performance. For a small example project, an official language image with a recent tag is usually enough. Alpine based images are often preferred for their smaller size, but they might require extra setup for native dependencies.
You do not need to understand every possible base image at this point, but you must select one that matches the language and runtime that your simple web app needs.
Writing a Basic Dockerfile for the App
With the base image chosen, you can write a minimal Dockerfile that turns your source code into a runnable container. The structure will follow the same basic pattern you learned in the Dockerfile chapters, but here the focus is on expressing your web app’s startup process.
A typical flow for a simple app is:
Start from the chosen base image with a FROM instruction.
Set a working directory inside the container.
Copy the application files into that directory.
Install dependencies inside the container.
Expose the port that the app uses.
Specify the default command or entrypoint that starts the web server.
Each of these steps mirrors something you would normally do on your host machine. The Dockerfile converts these manual steps into an image build recipe.
If your application uses configuration files or static assets such as HTML or CSS, you will usually copy them into the image in the same stage as your source code, so they are available when the container runs.
Building the Image from the Project
Once you have a Dockerfile in the project folder, you can build your first image for the web app. You will use docker build from the root of the project, where the Dockerfile resides, and tag the resulting image with a simple name.
During this build, Docker executes the instructions from your Dockerfile, creating layers at each step. For a small application this process will usually be quick, although dependency installation may still take some time.
It is common to rebuild the image several times as you refine the Dockerfile. For example, you might adjust which files are copied, tweak the working directory, or alter the command that starts the server. Each time you rebuild, Docker will reuse any layers that have not changed, which speeds up the feedback loop.
Running the Web App Container
After a successful build, you can run your containerized web app with docker run. This is where you turn the image into a live container that listens on a port and can be reached from your browser.
Running a web app container always involves two key elements.
First, you must publish or map the container’s internal port to a port on your host, so you can open it in a browser. If your app listens on port 8000 inside the container, you might map it to port 8000 or 8080 on your host.
Second, you must use the correct image tag that you built. If you built an image called simple-web-app, then you run that exact name in your command.
If everything is configured correctly, the container will start, and your web app will respond on the mapped host port just as it did when you ran it directly on the host.
If your web app returns responses on the host but not inside Docker, the first things to check are the port mapping and the port the app is actually listening on inside the container.
Managing Source Code and Changes
When you make changes to the application code, you must decide how those changes reach the running container. For a very simple example that you share or deploy, you will usually rebuild the image and restart the container with the updated image.
For active development, you might use bind mounts to keep the container in sync with your source directory, but that pattern belongs to development workflows more generally. For a small demo web app intended as a teaching project, the simpler approach is to rebuild the image after each set of code changes.
This rebuild process helps reinforce the idea that a container image is a snapshot of your code and dependencies at a point in time. Whenever the code changes, you can create a new snapshot by rebuilding.
Basic Configuration inside the Container
Even very simple web apps often rely on some configuration values, such as the port number, a debug flag, or the environment mode. When Dockerizing the app, you will usually pass these values into the container as environment variables at runtime rather than hardcoding them in the image.
Your Dockerfile should contain the general logic of how the app runs, while docker run arguments such as -e for environment variables allow you to define settings that might change between environments. For a beginner level simple app, this might only mean choosing the port or whether to run in development or production mode.
If your application reads configuration from a .env file or similar mechanism, you can still use that approach, but you need to ensure that the file is present in the container or that you pass equivalent values through Docker when you start the container.
Testing the Dockerized Web App
Once your container is running and the port is mapped, you should test the web app through the browser or with an HTTP client, to ensure it behaves as expected in its containerized form.
The basic checks for a simple project are:
Confirm that you can reach the app using the host port you published.
Verify that the main endpoint returns the expected response.
Observe the logs inside Docker to see startup messages and any errors.
Stop and restart the container to ensure it behaves consistently.
If your app uses static files, templates, or simple routing, verify that each piece works just as it did when running directly on the host. This gives you confidence that the Dockerized version is a faithful representation of your original simple web app.
Sharing and Reusing the Dockerized App
One of the main advantages you gain by Dockerizing even a tiny web application is repeatability. Anyone with Docker installed can pull your image from a registry or build it from your Dockerfile and run the app with a single command.
For a learning project, you may keep the image local. For broader sharing, you would eventually push it to a registry such as Docker Hub under a repository name that others can access. Consumers then do not need any knowledge of the underlying programming language or dependency installation. They only need Docker and the image tag.
This separation of code runtime from the host environment is the central benefit you see clearly when Dockerizing a simple app. The same container can run on different operating systems and machines, and produce the same behavior, as long as Docker itself is available.