Dockerfile Essentials: Defining and Building Containerized Apps
10:37, 29.05.2026
Docker is a platform needed for running and, more importantly, for building containerized apps. With the help of containers, it is possible to pack the dependencies, source code, and runtime environment. This can be done via the Docker installation on your machine or a Kubernetes cluster for the infrastructure.
When the containers are launched, there is a necessity for the container image. The images specify the initial state of container filesystems. This can be done with the Dockerfile, and in the article, we will guide you through the process of building containerized apps and some helpful practices.
Introduction to Dockerfile
Dockerfiles include instructions in the form of text that are needed for building the container image. To process all the instructions in the file for the image assembling, you should use the following command:
docker build
After the usage of this command, all the instructions are processed sequentially. When the line starts with #, it is not a command but a comment or interpretation of the command. The arguments in the lines can be divided with a backslash.
Essential Instructions Used in a Dockerfile
There are more than 15 instructions that can be used in a Dockerfile for setting some configurations and adding content. Here is the list of the most popular ones:
- FROM is usually at the beginning of the file and is the basis for building.
- COPY is needed for adding folders/files to the filesystem. The copying process is done between the image and the Docker host.
- ADD works in a similar way to the above-mentioned instruction, but it also supports archive extractions and file URLs. The usage of ADD can simplify some tasks. For instance, archive files are extracted in the container instead of just copying them.
- RUN is used for the command running in the image. This means a new image layer is created with some needed changes. Usually, this instruction is necessary for the configuration and installation of the additional packages.
- ENV – is necessary for the setting of the environment.
Sample Workflow: Writing and Applying a Dockerfile
Now, when you understand some basic instructions, let's proceed to the actual sample of the workflow.
Start with the creation of the new directory by using the following code and saving it:
import { v4 as uuid } from "uuid";
console.log("Hello World");
console.log(`Your ID is ${uuid()}`);
Then add the package to the projects as follows:
$ npm install uuidThe following step will be adding the next instructions and saving them in Dockerfile:
FROM node:16
WORKDIR /app
COPY package.json .
COPY package-lock.json .
RUN npm install
COPY main.js .
ENTRYPOINT ["node"]
CMD ["main.js"]
Let’s try to explain some details of the code:
- node:16 – the official image that is used as a base.
- WORKDIR – in this part, the directory is changed to /app.
- COPY is used for adding 2 files from the host directory to the in-container working directory.
- npm install – is used for installation in the container’s filesystem for fetching the dependencies.
- COPY main.js – source code is copied to the container.
Steps to Build Your Dockerfile
At this point, you can start building an image from Dockerfile with the usage of the following command:
$ docker build -t demo-image:latest .
Once the image is built, you will see the instructions on the terminal.
Options Available During Docker Build
When using docker build command, you are specifying the paths to which you reference in the file. Paths outside the context won’t be noticeable for most instructions.
Docker automatically checks instructions according to the working directory, but it is possible to reference a different file with -f, like the following:
docker build -f dockerfiles/app.dockerfile -t demo-image:latest -t demo-image:v1.0 .
Running and Using the Created Image
To run the created image, use the following:
docker run demo-image:latest node main.js
Recommended Dockerfile Practices
Writing a Dockerfile is considered to be a fairly simple task, but some recommendations should be taken into consideration to improve performance, usability, and security level.
1. Avoid Using `latest` Tag for Base Images
In the FROM instruction, it is better to avoid the usage of ‘latest’ because it can lead to some unplanned changes. Most image authors immediately use the latest version. When rebuilding the image, it could unnoticeably provoke the usage of a different version, causing some issues.
That’s why it is better to be more specific and mention node:16.
2. Choose Base Images from Trusted Sources Only
The usage of a trusted source for the base image is highly important, otherwise, you can experience some security risks. An untrusted base image can include malware that functions outside the containers. That’s why it is better to use images that are published by verified users or are official.
3. Implement HEALTHCHECK to Monitor Container Status
It is highly important to monitor the container status. This could be done with orchestrators such as Kubernetes so that problematic containers can be automatically restarted.
HEALTHCHECK should be added to the Dockerfile so the command will run inside and check if everything functions properly:
HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD curl -f http://localhost || exit 1
The status of the containers can be checked with the ps command.
4. Configure ENTRYPOINT and CMD Properly
CMD and ENTRYPOINT instructions are somehow similar. CMD gives default arguments, and ENTRYPOINT runs this process. Arguments in CMD can be customized when containers are started with docker run.
5. Never Store Secrets Directly in Images
For the security of your secrets, it is not recommended to store API keys or passwords in the images. Because any user with access to the image can check this information and use it.
Also, it is better to use environment variables and not defaults in the Dockerfile. This will help to minimize some security risks.
6. Add Labels to Organize and Track Your Images
In case your team has lots of images, it is better to use the LABEL instruction. That means you can add valuable information for each project. Labels are set based on reverse DNS syntax as follows:
LABEL com.sample.team=backend
7. Run Containers Using a Non-Root User
According to the default characteristics, containers are running as the root user. This can cause some security risks, because hackers could enter the container and run some commands on the host.
This can be solved by adding a USER instruction to the Dockerfile. The best recommendation here is to use non-root users for all the files.
USER demo-app
USER 1000
USER demo-app:demo-group
8. Use `.dockerignore` to Speed Up the Build Process
Images are usually built with a work directory, and it can contain unnecessary directories and files. To improve the performance, it is needed to exclude the paths that are not needed. This will help to speed up the process when Docker copies the build context at the initial stage of the building process.
Use dockerignore file in the directory to exclude unnecessary directories/files.
9. Optimize Image Size Whenever Possible
The images can become extremely large, and that directly impacts the build time. To optimize the image size, it is advisable to use only the necessary packages and exclude all the rest. Also, it is better to use the compact base image in case that is possible. For instance, Alpine Linux instead of Ubuntu.
10. Use Linters and Vulnerability Scanners for Security
Dockerfiles might include errors that can lead to serious problems or unexpected behaviors. It is possible to use linters such as Hadolint for checking the possible issues.
To run it use the following command:
$ docker run --rm -i hadolint/hadolint < Dockerfile
Also, it is recommended to use such scanners as Trivy that will help with finding the outdated packages. Try to use it before the deployment to minimize some risks.
Summary of Key Takeaways
Docker is an extremely popular technology, and it directly impacts the software delivery by the usage of the containers that work perfectly on various environments. For the proper usage of Docker, it is necessary to write a Dockerfile that includes all the necessary instructions. The instructions are defined by the OCI image specification and create images that are needed for the OCI-compatible container runtime.