On this page
  1. Why is my Docker image so big?
  2. How do I find out what’s taking space?
  3. What does ‘it worked’ look like?
  4. Where AI coding assistants get this wrong
  5. Checklist
  6. FAQ
    1. Does a smaller image matter if I only deploy once?
    2. What is the difference between alpine and slim base images?
  7. Related topics
  8. Sources
tutorial

Why Is Your Docker Image So Large (and How Do You Shrink It)?

A 1.5 GB Docker image is usually build cruft, not your app. Learn the four causes and the fixes that routinely cut images by 80%.

Quick answer

  • Most of a fat Docker image is build tools and caches the final app never needs.
  • Multi-stage builds — build in one stage, copy only the artifacts into a slim runtime stage — are the single biggest win.
  • Check layer sizes with docker history; one unnecessary COPY or apt install can add hundreds of MB.

Why is my Docker image so big?

The usual causes, in order of size: you start from a fat base image (python:3.12 instead of python:3.12-slim or an alpine variant); you install build tools (compilers, package managers) that only exist to compile dependencies; you copy the whole project including node_modules or .venv; and you never clean apt or pip caches. Each layer is permanent — deleting a file in a later layer doesn’t remove it from the image, it just hides it. That’s why ‘I removed the cache’ sometimes changes nothing.

# Before: one big image with everything
FROM python:3.12
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt

# After: multi-stage — build tools never reach the runtime image
FROM python:3.12-slim AS builder
COPY requirements.txt /
RUN pip install --prefix=/install -r requirements.txt

FROM python:3.12-slim
COPY --from=builder /install /usr/local
COPY . /app
WORKDIR /app
CMD ["uvicorn", "app:app"]

How do I find out what’s taking space?

Run docker history –no-trunc to see each layer’s size, or docker images to compare image sizes after each change. The layers that show hundreds of MB are almost always a fat base image, a pip/npm install that pulled in build tools, or a COPY of a directory that shouldn’t be there. Add a .dockerignore that excludes node_modules, .venv, .git, and build output — without it, docker sends your whole project directory to the builder, including files the image will never use.

What does ‘it worked’ look like?

After the changes, docker images should show your image at a fraction of its previous size — commonly 100-300 MB instead of 1.5 GB for a Python or Node app — and the app must still build and run identically. Verify the container starts and the app responds before celebrating the smaller size; a shrunken image that crashes at startup is a regression, not an improvement.

Where this bites vibecoders

AI assistants generate working Dockerfiles that are almost never small: they copy the whole repo, install everything, and skip .dockerignore. The result is a 1.5 GB image that takes minutes to build and push, exhausts free container registries, and slows every deploy. Asking the assistant for a ‘multi-stage build with a slim base’ fixes most of it, and the size check (docker images) makes the improvement visible immediately.

Where AI coding assistants get this wrong

  • Using the full base image when a -slim variant works, adding hundreds of MB.
  • Copying the entire project including node_modules into the image.
  • Installing build tools in the final image instead of a builder stage.
  • No .dockerignore, so the build context includes junk and rebuilds are slow.

Checklist

  • Use a slim or alpine base image unless you need the full one.
  • Use multi-stage builds: compile in a builder, copy artifacts to runtime.
  • Add a .dockerignore excluding node_modules, .venv, .git, and build output.
  • Check docker history for fat layers and docker images for the final size.

FAQ

Does a smaller image matter if I only deploy once?

Less than for frequent deploys, but still yes: smaller images pull faster on cold starts, use less registry and disk space, and have a smaller attack surface. The effort is a one-time Dockerfile change, so the cost of doing it is near zero.

What is the difference between alpine and slim base images?

Alpine is a minimal Linux distribution using musl libc; slim variants are the official image with the documentation and common packages stripped out. Alpine images are smaller, but some Python/Node packages need musl-specific builds. Try slim first — it’s the same distro with less cruft and rarely breaks anything.

Sources

Share: