Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: September 10, 2025
In this tutorial, we’ll discuss adding or modifying the content of a file within a Docker image. There are three different ways of doing so: Generating an image from a modified container, modifying the file in place from the Dockerfile, and copying the file directly while building the image.
To illustrate the different cases, we’ll use the /etc/sysctl.conf file as the destination for the configuration option: kernel.core_uses_pid = 1. This is a useful setting for debugging multi-threaded applications; here it’s used as an example.
The first approach uses docker container commit. With the docker container commit command, we’ll generate an image based on an existing container.
Let’s start with the following Dockerfile:
$ cat Dockerfile
FROM alpine:latest
CMD ["sh"]
Next, we generate a base image from the Dockerfile with the docker build command:
$ docker build --tag image/base .
Docker is now aware of our image, as seen with docker image ls:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
image/base latest 62220e58e6c2 2 weeks ago 8.31MB
Then, it’s docker run to create a container based on image/base:
$ docker run -td image/base
1c7771ce9e2a38c9c80c420689833738c6601a04ce5536d73c090868c015f327
We’ve merged the -t option (equivalent to –tty) to allocate a pseudo-TTY, and the -d option (equivalent to –detach) to run the container in the background. The hexadecimal output is the container ID.
It’s important not to add the common –rm option since this will automatically remove the container and volumes when exiting. We need to keep the container to be able to generate an image from it. In fact, let’s check that our container exists with docker container ps:
$ docker container ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c7771ce9e2a image/base "sh" 10 seconds ago Up 10 seconds sleepy_vaughan
Now we need to add the content we want to the file:
$ docker exec -it 1c7771ce9e2a /bin/sh -c "cat /etc/sysctl.conf"
$ docker exec -it 1c7771ce9e2a /bin/sh -c "echo 'kernel.core_uses_pid = 1' >> /etc/sysctl.conf"
$ docker exec -it 1c7771ce9e2a /bin/sh -c "cat /etc/sysctl.conf"
kernel.core_uses_pid = 1
We have used the -i option to keep the STDIN attached and the -t option as before. We provide the ID of the container and use /bin/sh with the command we want to run after the -c option, surrounded by quotes.
Regarding the commands, the first one checks with cat that the /etc/sysctl.conf file is empty. This file may contain some commented lines based on the distribution used. Then, the echo command appends the text “kernel.core_uses_pid = 1” into the /etc/sysctl.conf file using pipe redirection. Lastly, cat ensures that the content of the file includes the new line we’ve added.
All these operations can be run interactively as well by accessing the shell in the container:
$ docker exec -it 1c7771ce9e2a /bin/sh
/# cat /etc/sysctl.conf
/# echo 'kernel.core_uses_pid = 1' > /etc/sysctl.conf
/# cat /etc/sysctl.conf
kernel.core_uses_pid = 1
Once we have a container with our modified content, we generate a new image with its ID and the image name we want:
$ docker commit 1c7771ce9e2a image/mod
sha256:9c57163be3f6402fe95b781468f1b18b5862deefacf9b54ce3ae7c20b56c8706
If we list the images with docker image ls, we’ll have the original image/base and the newly created image/mod:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
image/mod latest 9c57163be3f6 5 seconds ago 8.31MB
image/base latest 62220e58e6c2 2 weeks ago 8.31MB
Following our same steps as before reveals that our new image has the file already altered:
$ docker run -td image/mod
b7273b7cf3ca24eba1d44c2324179bc15053115ec6bde3d8edf2bd63720bdaa5
$ docker exec -it b7273b7cf3ca /bin/sh -c "cat /etc/sysctl.conf"
kernel.core_uses_pid = 1
Once we’re happy with our previous procedure, let’s automate it for repeatability. The Dockerfile can also contain some text-editing actions inside it.
Starting with a template from our previous Dockerfile, we use the RUN instruction to execute shell commands:
$ cat Dockerfile
FROM alpine:latest
RUN echo "kernel.core_uses_pid = 1" > /etc/sysctl.conf
CMD ["sh"]
Afterwards, the docker build command generates an image based on this modified Dockerfile:
$ docker build --tag image/mod .
[+] Building 0.9s (6/6) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 127B 0.0s
=> [internal] load metadata for docker.io/library/alpine:latest 0.4s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> CACHED [1/2] FROM docker.io/library/alpine:latest@sha256:8a1f59ffb675680d47d 0.0s
=> [2/2] RUN echo "kernel.core_uses_pid = 1" >> /etc/sysctl.conf 0.3s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:83ddfd5f059a7a92d250dfaa11df14dfb42375a9753be1f271f9 0.0s
=> => naming to docker.io/image/mod 0.0s
docker image will list the images available in our system:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
image/mod latest 83ddfd5f059a 4 seconds ago 8.31MB
We now create a container with the image from the modified Dockerfile and inspect the /etc/sysctl.conf file with cat as done before:
$ docker run -td image/mod
d610219984dc30fa79fb0ba3a7724b13a1e2d643e459451c2c48b4713d1c9362
$ docker exec -it d610219984dc /bin/sh -c "cat /etc/sysctl.conf"
kernel.core_uses_pid = 1
The file contains the new kernel option, which was not part of the original file.
In the previous example, we added a single line to a file. However, many modifications will entail adding several lines to a file. Simply concatenating several RUN instructions will achieve this:
RUN echo "line1" >> /etc/sysctl.conf
RUN echo "line2" >> /etc/sysctl.conf
RUN echo "line3" >> /etc/sysctl.conf
RUN echo "line4" >> /etc/sysctl.conf
More complex operations are also possible. Let’s assume we want to replace some original_text in the /etc/systctl.conf file with a new_text. sed performs more complex operations from within the Dockerfile:
RUN sed -i 's/original_text/new_text/g' /etc/sysctl.conf
In this case, we’re using -i to perform the edition inline and avoid having temporary files. The leading s requests substitution, and the trailing g is to replace occurrences in the whole file.
Imagine we need to make multiple changes to a file or iterate on them. However, we want to take advantage of the perks that modern IDEs have.
We’ll make the changes in the files within our system and then transfer the file using the COPY command in the Dockerfile.
Let’s start with a custom sysctl.conf in our system already modified in our IDE:
$ cat custom_sysctl.conf
kernel.core_uses_pid = 1
Next, we add the COPY instruction in the Dockerfile to transfer the file to the image:
$ cat Dockerfile
FROM alpine:latest
COPY custom_sysctl.conf /etc/sysctl.conf
CMD ["sh"]
As before, the next step is to build the image based on the Dockerfile:
$ docker build --tag image/mod .
.[+] Building 0.5s (7/7) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 110B 0.0s
=> [internal] load metadata for docker.io/library/alpine:latest 0.4s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 39B 0.0s
=> [1/2] FROM docker.io/library/alpine:latest@sha256:8a1f59ffb675680d47db6337b4 0.0s
=> CACHED [2/2] COPY custom_sysctl.conf /etc/sysctl.conf 0.0s=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:6982185cb145573f62b1656fbe3c90cb9d110fa820596adc245b 0.0s
=> => naming to docker.io/image/mod 0.0s
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
image/mod latest 6982185cb145 15 seconds ago 8.31MB
Finally, we run a container and inspect the file that we copied to ensure it’s right:
$ docker run -td image/mod
a2aa891cd796b48eefd15176b1d2e165995d646aa50f035c29ca9a5422ae8408
$ docker exec -it a2aa891cd796 /bin/sh -c "cat /etc/sysctl.conf"
kernel.core_uses_pid = 1
This is a convenient solution that allows us to interactively edit a file and then copy it to the destination path.
In this article, we discussed three solutions for editing specific files and incorporating them into our Docker image.
In the first solution, we modified the files inside a Docker container and generated an image from it using the docker container commit command.
For the second solution, we used RUN inside the Dockerfile to execute shell commands that manipulated the file for us.
As the last solution, we edited the file in our system in our favorite IDE and copied it via the COPY instruction in a Dockerfile.