How to force Docker to rerun `apt-get update`?
Some security updates have just come out and I want to rebuild my Docker images to take advantage of the updates.
However when I run docker build .
it completes immediately without updating anything because nothing has changed in the Dockerfile
, and everything is cached. It doesn't even try to run the apt-get update
line in my Dockerfile
.
How can I force Docker to run the apt-get update
command again, even though nothing has changed?
There is a --no-cache
option that says it won't use the cache during the build, but I want it to use the cache for the commands before apt-get update
and I want the results saved into the cache for the next run (replacing the currently cached images), so I definitely want to be using the cache.
I also can't use docker rmi
to remove the image generated at the point after apt-get
has been run, because it refuses to delete this image as the image has dependent child images
.
You can try something like the following:
FROM ubuntu:16.04
# LAYER 1
RUN echo "$(date), layer1" > /tmp/cache.txt
# LAYER 2
RUN echo "$(date), layer2" >> /tmp/cache.txt
# LAYER 3
ARG FORCE_UPDATE=no
RUN echo "$(date), layer3" >> /tmp/cache.txt
# LAYER 4
RUN echo "$(date), layer4" >> /tmp/cache.txt
CMD ["cat", "/tmp/cache.txt"]
Build image for the first time
$ docker build -t serverfault:900445 .
Sending build context to Docker daemon 2.048kB
Step 1/7 : FROM ubuntu:16.04
---> 0458a4468cbc
Step 2/7 : RUN echo "$(date), layer1" > /tmp/cache.txt
---> Running in ac7f6b1e915a
Removing intermediate container ac7f6b1e915a
---> 42a6d14cc4cc
Step 3/7 : RUN echo "$(date), layer2" >> /tmp/cache.txt
---> Running in ba4cf5b54c35
Removing intermediate container ba4cf5b54c35
---> 783957979b21
Step 4/7 : ARG FORCE_UPDATE=no
---> Running in 818fd652d5cb
Removing intermediate container 818fd652d5cb
---> b8afb473cd9d
Step 5/7 : RUN echo "$(date), layer3" >> /tmp/cache.txt
---> Running in 38c0e6cbb94e
Removing intermediate container 38c0e6cbb94e
---> 03ac41df5bfa
Step 6/7 : RUN echo "$(date), layer4" >> /tmp/cache.txt
---> Running in 0294b5a4078e
Removing intermediate container 0294b5a4078e
---> 141667a2d5f3
Step 7/7 : CMD ["cat", "/tmp/cache.txt"]
---> Running in 86b852d8222d
Removing intermediate container 86b852d8222d
---> dcd57aca0c25
Successfully built dcd57aca0c25
Successfully tagged serverfault:900445
As we can see all layers were built. Run one more time
$ docker build -t serverfault:900445 .
Sending build context to Docker daemon 2.048kB
Step 1/7 : FROM ubuntu:16.04
---> 0458a4468cbc
Step 2/7 : RUN echo "$(date), layer1" > /tmp/cache.txt
---> Using cache
---> 42a6d14cc4cc
Step 3/7 : RUN echo "$(date), layer2" >> /tmp/cache.txt
---> Using cache
---> 783957979b21
Step 4/7 : ARG FORCE_UPDATE=no
---> Using cache
---> b8afb473cd9d
Step 5/7 : RUN echo "$(date), layer3" >> /tmp/cache.txt
---> Using cache
---> 03ac41df5bfa
Step 6/7 : RUN echo "$(date), layer4" >> /tmp/cache.txt
---> Using cache
---> 141667a2d5f3
Step 7/7 : CMD ["cat", "/tmp/cache.txt"]
---> Using cache
---> dcd57aca0c25
Successfully built dcd57aca0c25
Successfully tagged serverfault:900445
And now all layers were taken from cache. Simple check
$ docker run -it --rm serverfault:900445
Wed Mar 7 15:44:22 UTC 2018, layer1
Wed Mar 7 15:44:23 UTC 2018, layer2
Wed Mar 7 15:44:24 UTC 2018, layer3
Wed Mar 7 15:44:25 UTC 2018, layer4
Now if you need to force update cache for some specific layer use the following
$ docker build --build-arg FORCE_UPDATE=$(date '+%s') -t serverfault:900445 .
Sending build context to Docker daemon 2.048kB
Step 1/7 : FROM ubuntu:16.04
---> 0458a4468cbc
Step 2/7 : RUN echo "$(date), layer1" > /tmp/cache.txt
---> Using cache
---> 42a6d14cc4cc
Step 3/7 : RUN echo "$(date), layer2" >> /tmp/cache.txt
---> Using cache
---> 783957979b21
Step 4/7 : ARG FORCE_UPDATE=no
---> Using cache
---> b8afb473cd9d
Step 5/7 : RUN echo "$(date), layer3" >> /tmp/cache.txt
---> Running in f8ad1cd195eb
Removing intermediate container f8ad1cd195eb
---> b22972691095
Step 6/7 : RUN echo "$(date), layer4" >> /tmp/cache.txt
---> Running in 9994175a082e
Removing intermediate container 9994175a082e
---> 7ed42904373f
Step 7/7 : CMD ["cat", "/tmp/cache.txt"]
---> Running in 67de76e45d43
Removing intermediate container 67de76e45d43
---> 833f3faf9fd7
Successfully built 833f3faf9fd7
Successfully tagged serverfault:900445
As you can see layers 1,2 were taken from cache but layer 3 and all latest layers were rebuilt
$ docker run -it --rm serverfault:900445
Wed Mar 7 15:44:22 UTC 2018, layer1
Wed Mar 7 15:44:23 UTC 2018, layer2
Wed Mar 7 15:45:35 UTC 2018, layer3
Wed Mar 7 15:45:35 UTC 2018, layer4
Repeat one more time
$ docker build --build-arg FORCE_UPDATE=$(date '+%s') -t serverfault:900445 .
Sending build context to Docker daemon 2.048kB
Step 1/7 : FROM ubuntu:16.04
---> 0458a4468cbc
Step 2/7 : RUN echo "$(date), layer1" > /tmp/cache.txt
---> Using cache
---> 42a6d14cc4cc
Step 3/7 : RUN echo "$(date), layer2" >> /tmp/cache.txt
---> Using cache
---> 783957979b21
Step 4/7 : ARG FORCE_UPDATE=no
---> Using cache
---> b8afb473cd9d
Step 5/7 : RUN echo "$(date), layer3" >> /tmp/cache.txt
---> Running in 618880ba45be
Removing intermediate container 618880ba45be
---> b0512372ddfd
Step 6/7 : RUN echo "$(date), layer4" >> /tmp/cache.txt
---> Running in 0cb552431048
Removing intermediate container 0cb552431048
---> 61be6f0c0f21
Step 7/7 : CMD ["cat", "/tmp/cache.txt"]
---> Running in 5f9ee850c28e
Removing intermediate container 5f9ee850c28e
---> ac73b7754107
Successfully built ac73b7754107
Successfully tagged serverfault:900445
$ docker run -it --rm serverfault:900445
Wed Mar 7 15:44:22 UTC 2018, layer1
Wed Mar 7 15:44:23 UTC 2018, layer2
Wed Mar 7 15:46:10 UTC 2018, layer3
Wed Mar 7 15:46:11 UTC 2018, layer4
Another way which I have taken to doing is using the LABEL
command:
FROM ...
# Update this date to re-run apt-get.
LABEL package.date=2021-09-12
RUN apt-get ...
Then any time the date in the label is changed, every command after that runs again. As a bonus, the date gets built into the image so you can retrieve it with docker inspect --format '{{ index .Config.Labels "package.dates" }}' <container>
so you can check your images to find any that have not had any security updates for a while, even if they have been rebuilt recently.
Another trick which may save time updating packages is to update the base image first. With a Dockerfile like this:
FROM debian:stable
...
You can run docker pull debian:stable
to update that tag/image to the latest version. When you next build the Docker image, it will start with that new version and rebuild everything after it because there are not yet any cached layers starting from that new base image.
Typically the base images are updated fairly regularly to include the latest packages, so updating that first will usually result in a smaller number of packages that the following apt-get update
needs to download.