E: Unable to locate package curl in a Dockerfile almost always means the
RUN line calls apt-get install without an apt-get update
before it in the same layer. The package exists; the image simply ships with no package lists,
so apt has nothing to search.
Put apt-get update && apt-get install -y ... in one RUN
instruction and the build succeeds.
The error
#5 [2/2] RUN apt-get install -y curl
#5 0.191 Reading package lists...
#5 0.199 Building dependency tree...
#5 0.199 Reading state information...
#5 0.200 E: Unable to locate package curl
#5 ERROR: process "/bin/sh -c apt-get install -y curl" did not complete successfully: exit code: 100
The Dockerfile that produced it was two lines: FROM ubuntu:24.04 and
RUN apt-get install -y curl. The build stops with exit code 100, apt's code for
a failed operation.
Why it happens
Official Debian and Ubuntu base images are kept small by deleting
/var/lib/apt/lists/*, the downloaded index of what each repository offers.
Without that index apt knows no packages at all, so curl, git or
anything else is "unable to locate". Notice that the step took 0.2 seconds: apt never
touched the network.
Docker caches each instruction as a layer, so a separate RUN apt-get update
line is a layer of its own that a later build can reuse unchanged. With both commands in
one RUN, the lists are fetched by the same step that installs, as the fixed
build below shows. In that build the install step took about 17 seconds, most of it
downloading the lists and the packages.
The fix
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
With this Dockerfile the same build fetched the lists, installed
curl (8.5.0-2ubuntu10.16) and finished. --no-install-recommends
keeps optional extras out, and removing the lists at the end of the same line keeps the
layer as small as the base image intended. If the package name itself is wrong, the same
message appears even after an update; check the name with apt-cache search
inside a container started from the base image.
How it was reproduced
A folder with only the two-line Dockerfile above, built with
docker build --no-cache --progress=plain -t fix8-bld-p1 fix8-bld-p1 on Docker
Desktop with engine 29.9.2 and the ubuntu:24.04 image. The fixed Dockerfile
was built the same way into fix8-bld-p1fix; the folder and tag names are this
test's own.
Frequently asked
- Why does apt-get say unable to locate package in Docker?
- The base image ships without package lists to stay small. Run apt-get update before apt-get install, in the same RUN instruction, so apt knows which packages the repositories offer.
- Does apt-get update need to be in the same RUN line as apt-get install?
- Put them in one RUN joined with &&. Each RUN is its own cached layer, and one line guarantees the lists are fetched by the step that installs.
- Why do Dockerfiles end apt-get install with rm -rf /var/lib/apt/lists?
- The lists are only needed while installing. Deleting them in the same RUN keeps them out of the image layer, which keeps the image smaller.
More decoded errors in the Fixes category. For writing a whole Dockerfile for a .NET app, see building an image for a .NET API.