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 top

Example: Install Tools Inside Ubuntu

Start a container:

docker run -it --name ubuntu-lab ubuntu:24.04 bash

Inside the container:

apt-get update
apt-get install -y python3 git
 
python3 --version
git --version

Python 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 --version

If you create a new container from the original image, the changes are not there:

docker run -it ubuntu:24.04 bash
python3 --version
git --version

That 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 git

This 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 --version

This 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 bash

This is closest to “Save As” in a file explorer.

changed container
      |
      | docker commit
      v
new image snapshot

But it is not the best habit for normal builds.

MethodGood ForWeakness
Dockerfile + docker buildRepeatable images, team use, production-like workflowsRequires writing the steps clearly
docker commitQuick debug snapshot, experiments, preserving manual changesHides the exact steps that created the image

Warning: docker commit does 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:

  1. Experiment in a temporary container.
  2. Figure out the commands that work.
  3. Put those commands into a Dockerfile.
  4. Build a new image.
  5. 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

ActionAre Python/Git Still There?Why
Exit shell, then docker start -ai ubuntu-labYesSame container, same writable layer
docker stop ubuntu-lab, then docker start -ai ubuntu-labYesSame container again
docker rm ubuntu-labNoContainer writable layer is removed
docker run -it ubuntu:24.04 bashNoNew container from original image
docker commit ubuntu-lab ubuntu-python-git:manualYes, in new imageSnapshot of container filesystem changes
docker build -t ubuntu-python-git:1.0 .Yes, in new imageDockerfile 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 commit is 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.