If you start a container from ubuntu:24.04, install Python and Git inside it, those tools work inside that container. The change is real, but it is not automatically baked back into the original Ubuntu image.
The key idea:
Image: read-only template
Container: running instance with a writable layer on topExample: Install Tools Inside Ubuntu
Start a container:
docker run -it --name ubuntu-lab ubuntu:24.04 bashInside the container:
apt-get update
apt-get install -y python3 git
python3 --version
git --versionPython and Git now work in that container session.
What Persists?
The installed packages are written into the container’s writable layer.
Container: ubuntu-lab
+----------------------------------+
| writable layer |
| python3 + git installed here |
+----------------------------------+
| image layers from ubuntu:24.04 |
+----------------------------------+If you exit and restart the same container, the changes are still there:
exit
docker start -ai ubuntu-lab
python3 --version
git --versionIf you create a new container from the original image, the changes are not there:
docker run -it ubuntu:24.04 bash
python3 --version
git --versionThat new container starts from the unchanged ubuntu:24.04 image.
Lifecycle View
flowchart TD Image["ubuntu:24.04 image"] --> Container["docker run creates container"] Container --> Install["Install python3 and git inside container"] Install --> Writable["Changes live in container writable layer"] Writable --> Same["Restart same container: tools still available"] Writable --> New["Run new container from ubuntu:24.04"] New --> Missing["Tools are missing again"] Writable --> Commit["Option: docker commit"] Commit --> ManualImage["New image snapshot"] Writable --> Dockerfile["Preferred: write Dockerfile"] Dockerfile --> Build["docker build"] Build --> RepeatableImage["Repeatable new image"]
Why the Original Image Does Not Change
Images are immutable. A running container can write files, install packages, and change configuration, but those changes belong to that container’s writable layer.
The original image remains the same:
ubuntu:24.04 image
read-only base layers
ubuntu-lab container
ubuntu:24.04 image layers
+ writable layer with python3 and gitThis is useful because many containers can start from the same image without mutating the image for everyone else.
Option 1: Preferred Path - Update the Dockerfile
For anything repeatable, write the change into a Dockerfile.
FROM ubuntu:24.04
RUN apt-get update && \
apt-get install -y --no-install-recommends python3 git && \
rm -rf /var/lib/apt/lists/*
CMD ["bash"]Build a new image:
docker build -t ubuntu-python-git:1.0 .Run it:
docker run -it --rm ubuntu-python-git:1.0
python3 --version
git --versionThis is the normal workflow because the steps are visible, reviewable, and repeatable.
Option 2: Quick Snapshot - docker commit
docker commit creates a new image from a container’s filesystem changes.
docker commit ubuntu-lab ubuntu-python-git:manual
docker run -it --rm ubuntu-python-git:manual bashThis is closest to “Save As” in a file explorer.
changed container
|
| docker commit
v
new image snapshotBut it is not the best habit for normal builds.
| Method | Good For | Weakness |
|---|---|---|
Dockerfile + docker build | Repeatable images, team use, production-like workflows | Requires writing the steps clearly |
docker commit | Quick debug snapshot, experiments, preserving manual changes | Hides the exact steps that created the image |
Warning:
docker commitdoes not include data stored in mounted volumes. It captures the container filesystem changes, not external volume data.
What Usually Happens in Real Work?
People usually do this:
- Experiment in a temporary container.
- Figure out the commands that work.
- Put those commands into a Dockerfile.
- Build a new image.
- Throw away the temporary container.
Example:
Try manually:
apt-get update
apt-get install -y python3 git
Then encode repeatably:
RUN apt-get update && \
apt-get install -y --no-install-recommends python3 git && \
rm -rf /var/lib/apt/lists/*That keeps the learning workflow fast without making the final image mysterious.
Same Container vs New Container
| Action | Are Python/Git Still There? | Why |
|---|---|---|
Exit shell, then docker start -ai ubuntu-lab | Yes | Same container, same writable layer |
docker stop ubuntu-lab, then docker start -ai ubuntu-lab | Yes | Same container again |
docker rm ubuntu-lab | No | Container writable layer is removed |
docker run -it ubuntu:24.04 bash | No | New container from original image |
docker commit ubuntu-lab ubuntu-python-git:manual | Yes, in new image | Snapshot of container filesystem changes |
docker build -t ubuntu-python-git:1.0 . | Yes, in new image | Dockerfile builds a repeatable image |
TL;DR
- Installing software inside a running container works for that container.
- The original image does not change.
- Restarting the same container keeps its writable-layer changes.
- Creating a new container from the old image does not include those changes.
docker commitis the quick “Save As” option, useful for snapshots.- A Dockerfile is the recommended way to update an image because it is repeatable and reviewable.
Resources
Docker: What is an image? Official explanation of image immutability and image layers.
Docker storage drivers Official explanation of image layers and the container writable layer.
docker container commit Official reference for creating an image from container changes.
Dockerfile overview Official Dockerfile guidance for building repeatable images.