
Most Docker confusion starts here:
“I ran a container… but where did it come from?”
The answer is Docker images.
If you don’t deeply understand images, Docker will always feel half‑clear.
This post explains Docker images end‑to‑end, using real commands, exactly how images are built, shared, and used in real projects.
Mental Model First (Very Important)
Container ≠ Image
- Image → blueprint (read‑only)
- Container → running instance of an image
You build images.
You run containers.
Everything else in Docker revolves around this.
Step 1: Pull an Image (What docker pull Really Does)
Let’s start clean.
docker pull nginx
What actually happened?
Docker:
- Contacted Docker Hub (default registry)
- Downloaded image layers
- Stored them locally
Verify:
docker images
You’ll see something like:
REPOSITORY TAG IMAGE ID SIZE
nginx latest abc123 180MB
✅ You now have an image
❌ No container yet
Step 2: Run the Image (Image → Container)
docker run -d -p 8080:80 nginx
What Docker did internally:
- Took the nginx image
- Created a container layer on top
- Started the main process (
nginx) - Attached networking
Check:
docker ps
This is the same image, but a new container.
Step 3: Images Are Immutable (This Changes Everything)
Try this:
docker exec -it <container_id> /bin/sh
Inside container:
echo "hello" > /test.txt
exit
Remove container:
docker rm -f <container_id>
Run again:
docker run -it nginx /bin/sh
cat /test.txt
❌ File is gone.
Why?
Because:
- Image = read‑only
- Container layer = disposable
✅ Images do not change
✅ Containers are temporary
This is intentional design, not a bug.
Step 4: Build Your Own Image (The Real Docker Skill)
Now let’s stop using other people’s images.
Create a simple app
mkdir myapp
cd myapp
Create index.html:
<h1>Hello from InfraDecode</h1>
Create Dockerfile (no extension):
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
Build the image
docker build -t infradecode-nginx .
Breakdown:
docker build→ build image-t→ tag (name).→ current directory as build context
Verify:
docker images
✅ You created your own image
Step 5: Run Your Custom Image
docker run -d -p 8080:80 infradecode-nginx
Open browser:
http://localhost:8080
✅ This container is running your image, not nginx’s default one.
Step 6: Image Layers (Why Docker Is Fast)
Run:
docker history infradecode-nginx
You’ll see:
- base image layers
- your COPY layer
Key idea: ✅ Docker caches layers
✅ Only changed layers rebuild
This is why Docker builds are fast when done correctly.
Step 7: Tag the Image (Before Pushing)
Docker registries require a namespace.
Format:
registry_name/<image>:<tag>
Example:
docker tag infradecode-nginx infra/infradecode-nginx:v1
Verify:
docker images
Same image ID — different name.
Step 8: Push Image to Docker Hub (Real Sharing)
Login first:
docker login
Push:
docker push infra/infradecode-nginx:v1
What happened:
- Docker uploaded layers
- Registry now stores your image
✅ Anyone with access can now pull this image
✅ This is how CI/CD and production work
Step 9: Pull and Run Anywhere (The Real Power)
On any machine with Docker:
docker pull infra/infradecode-nginx:v1
docker run -d -p 8080:80 infra/infradecode-nginx:v1
Same app.
Same behavior.
Different machine.
This is Docker’s real value.
Step 10: Real‑World Flow (How Teams Actually Use Docker)
This is the actual lifecycle:
Code → Dockerfile → docker build
→ docker push (registry)
→ docker pull (server)
→ docker run
Developers:
- build locally
- push images
Servers:
- pull images
- run containers
No SCP.
No “works on my machine”.
Common Beginner Mistakes (That Cause Pain)
❌ Editing inside containers
❌ Expecting containers to persist changes
❌ Not tagging images properly
❌ Using latest everywhere
❌ Building images without understanding layers
Docker is simple — but strict.
InfraDecode Takeaway
Docker images are the contract.
Containers are just temporary executions of that contract.
Once you respect that boundary, Docker becomes predictable, scalable, and boring — in the best way.
— InfraDecode
Discover more from
Subscribe to get the latest posts sent to your email.
