๐Ÿ”ง How I Patched a Docker Image Without Breaking Its Original Behavior

โ€œWhen you want to tweak a Docker image but treat its structure like a sacred temple.โ€
Docker patch blog thumbnail

๐Ÿงฉ The Problem

I had to make a minor update to an existing Docker image โ€” just a small edit to the /etc/hosts file. Easy, right?

But hereโ€™s the catch:

Classic case of โ€œsneak in, do the job, leave no trace.โ€


๐Ÿ› ๏ธ My Goal


๐Ÿงญ The Step-by-Step Journey

๐Ÿ” Step 1: Find the Original ENTRYPOINT and CMD

docker inspect nginx:latest --format='ENTRYPOINT={{json .Config.Entrypoint}} CMD={{json .Config.Cmd}}'

Letโ€™s say the output was:

ENTRYPOINT=["/docker-entrypoint.sh"]
CMD=["nginx","-g","daemon off;"]

๐Ÿงฑ Step 2: Create a Container Without Triggering ENTRYPOINT

docker create --name temp-container --entrypoint /bin/sh nginx:latest

OR

docker run -itd --name temp-container --entrypoint=sh nginx:latest

๐Ÿ”ง Step 3: Start the Container and Patch /etc/hosts

docker start temp-container
docker exec -it temp-container /bin/sh

Inside the container:

echo "192.168.1.100 mycustomhost" >> /etc/hosts
exit

๐Ÿ“ฆ Step 4: Commit It โ€” With ENTRYPOINT and CMD Restored

docker commit --change 'ENTRYPOINT ["/docker-entrypoint.sh"]' \
  --change 'CMD ["nginx","-g","daemon off;"]' \
  temp-container nginx:latest-new

๐Ÿงน Step 5: Cleanup

docker rm -f temp-container

โœ… Verification: Trust, But Verify

๐Ÿ”ธ Check ENTRYPOINT and CMD

docker inspect nginx:latest-new --format='ENTRYPOINT={{json .Config.Entrypoint}} CMD={{json .Config.Cmd}}'

๐Ÿ”ธ Check the /etc/hosts

docker run --rm nginx:latest-new cat /etc/hosts

๐Ÿ”ธ Full Behavior Test

docker run --rm nginx:latest-new

๐ŸŽฏ Why This Approach Rocks


๐Ÿงฐ Bonus: Automate It with a Script?

Hereโ€™s a teaser for a future post: Iโ€™ve built a shell script that automates this entire process โ€” from inspecting to committing โ€” for any image.

๐Ÿš docker-image-patcher.sh

#!/bin/bash

# Usage: ./docker-image-patcher.sh <image-name> <new-image-tag> <host-entry>
# Example: ./docker-image-patcher.sh nginx:latest nginx:patched "192.168.1.100 mycustomhost"

set -e

IMAGE_NAME="$1"
NEW_IMAGE_TAG="$2"
HOST_ENTRY="$3"

if [[ -z "$IMAGE_NAME" || -z "$NEW_IMAGE_TAG" || -z "$HOST_ENTRY" ]]; then
  echo "Usage: $0 <image-name> <new-image-tag> <host-entry>"
  exit 1
fi

TEMP_CONTAINER="temp-container-$$"

echo "๐Ÿ” Inspecting original image: $IMAGE_NAME"

ORIGINAL_ENTRYPOINT=$(docker inspect "$IMAGE_NAME" --format='{{json .Config.Entrypoint}}')
ORIGINAL_CMD=$(docker inspect "$IMAGE_NAME" --format='{{json .Config.Cmd}}')

echo "๐Ÿ“‹ Original ENTRYPOINT: $ORIGINAL_ENTRYPOINT"
echo "๐Ÿ“‹ Original CMD: $ORIGINAL_CMD"

echo "๐Ÿšง Creating temp container: $TEMP_CONTAINER"
docker create --name "$TEMP_CONTAINER" --entrypoint /bin/sh "$IMAGE_NAME" > /dev/null

echo "๐Ÿš€ Starting and patching /etc/hosts"
docker start "$TEMP_CONTAINER" > /dev/null
docker exec "$TEMP_CONTAINER" sh -c "echo '$HOST_ENTRY' >> /etc/hosts"

echo "๐Ÿ“ฆ Committing new image: $NEW_IMAGE_TAG"
docker commit \
  --change "ENTRYPOINT $ORIGINAL_ENTRYPOINT" \
  --change "CMD $ORIGINAL_CMD" \
  "$TEMP_CONTAINER" "$NEW_IMAGE_TAG" > /dev/null

echo "๐Ÿงน Cleaning up temp container"
docker rm -f "$TEMP_CONTAINER" > /dev/null

echo "โœ… Image $NEW_IMAGE_TAG created with patched /etc/hosts"

๐Ÿ Final Thoughts

Sometimes, the best kind of Docker change is the one thatโ€™s invisible to the rest of your system.

This technique lets you make surgical tweaks without blowing up the imageโ€™s original purpose โ€” a powerful skill when you're working with shared base images in large CI/CD pipelines.