1.1.2.1 Dependency Packaging
A focused guide to Dependency Packaging, connecting core concepts with practical Docker and container operations.
Dependency packaging is the practice, enabled by Docker, of bundling an application together with every library, runtime, and system-level dependency it needs, so that the resulting image is self-contained and does not rely on anything being pre-installed on the host machine.
The Problem It Solves
Before containerization, deploying an application meant ensuring the target machine had the correct language runtime version, the correct shared libraries, and the correct set of installed packages — and that none of those conflicted with what other applications on the same machine required. Dependency packaging removes this coordination problem by making dependencies part of the shipped artifact instead of part of the host's configuration.
How a Dockerfile Expresses Dependencies
A Dockerfile declares dependencies explicitly, layer by layer, so the build process is reproducible from a clean state.
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
Each RUN instruction that installs dependencies produces a cached layer. As long as requirements.txt does not change, Docker reuses the cached layer on subsequent builds instead of reinstalling packages, which keeps iteration fast without sacrificing reproducibility.
docker build -t myapp:latest .
Isolating Dependency Versions Per Application
Because each image carries its own dependency set, two applications on the same host can require conflicting versions of the same library without interfering with each other. One container might run against libssl1.1 while another runs against a newer version, since each dependency tree is sealed inside its own image.
docker run -d --name app-a vendor/app-a:1.0
docker run -d --name app-b vendor/app-b:2.0
Multi-Stage Builds for Lean Dependency Sets
Multi-stage builds let a project separate build-time dependencies (compilers, build tools, dev headers) from runtime dependencies, so the final image only contains what is actually needed to execute the application.
FROM golang:1.22 AS build
WORKDIR /src
COPY . .
RUN go build -o app .
FROM debian:bookworm-slim
COPY --from=build /src/app /usr/local/bin/app
CMD ["app"]
The final image excludes the entire Go toolchain, reducing both image size and the attack surface exposed at runtime.
Inspecting Packaged Dependencies
Dependencies baked into an image can be inspected without modifying the running system, which is useful for auditing what an image actually contains.
docker run --rm myapp:latest pip list
docker history myapp:latest
Dependency Packaging and Supply Chain Integrity
Because the dependency set is fixed at build time and the resulting image is content-addressable (referenced by a digest), the same dependency packaging mechanism that provides convenience also provides traceability: a specific image digest corresponds to one exact, unchanging set of packaged dependencies.
docker inspect --format='{{.Id}}' myapp:latest