Docker

How to Fix "Docker build slow on step RUN apt-get update"

4 min read by DebuggedIt

Quick answer

The issue where a "Docker build is slow on step RUN apt-get update" occurs during image compilation when the Debian or Ubuntu package manager spends excessive...

The issue where a "Docker build is slow on step RUN apt-get update" occurs during image compilation when the Debian or Ubuntu package manager spends excessive time fetching package indexes from default remote mirrors. This delay stems from slow geographical mirror routing, un-cached package list downloads across builds, IPv6 timeout retries, or redundant layer invalidation. Optimizing repository mirrors, enabling BuildKit cache mounts, and structuring layer caching cuts build times drastically.

The Problem

When running docker build, the build process hangs or progresses at extremely low download speeds (e.g., bytes per second) during the apt-get update step:

#7 [2/5] RUN apt-get update && apt-get install -y curl
#7 0.512 Get:1 http://deb.debian.org/debian bookworm InRelease [151 kB]
#7 15.23 Get:2 http://deb.debian.org/debian-security bookworm-security InRelease [48.0 kB]
#7 45.89 Get:3 http://deb.debian.org/debian bookworm-updates InRelease [52.1 kB]
#7 120.4 Ign:1 http://deb.debian.org/debian bookworm InRelease...
#7 210.8 Fetching package indexes takes over 5 minutes...

In automated CI/CD environments, slow mirror responses can trigger runner timeouts, failing builds with connection errors:

Err:1 http://archive.ubuntu.com/ubuntu jammy InRelease
  Could not connect to archive.ubuntu.com:80 (91.189.91.38), connection timed out
W: Failed to fetch http://archive.ubuntu.com/ubuntu/dests/jammy/InRelease  Could not connect to archive.ubuntu.com:80

Why It Happens

The apt-get update command downloads package repositories from remote HTTP mirrors every time Docker invalidates the layer cache. Common bottlenecks include:

  • S3 / Mirror Geographic Latency: Default distribution mirrors (like deb.debian.org or archive.ubuntu.com) route traffic to distant, overloaded geographical mirrors.
  • IPv6 Network Timeouts: APT attempts to connect via IPv6 by default. If your network or Docker daemon lacks full IPv6 routing, APT hangs for 30-60 seconds waiting for connections to time out before falling back to IPv4.
  • No APT Package Caching: Docker builds execute in clean ephemeral layers, forcing APT to re-download package lists from scratch on every cache invalidation.
  • Layer Cache Invalidation: Ordering COPY . . before RUN apt-get update invalidates the layer cache on every local file edit.

The Fix

Implement these optimizations to drastically reduce build times during the package update phase.

Step 1: Force APT to use IPv4

Prevent long network timeouts by forcing APT to resolve repositories over IPv4 only. Add an APT configuration option inside your Dockerfile before running apt-get update:

RUN echo 'Acquire::ForceIPv4 "true";' > /etc/apt/apt.conf.d/99force-ipv4     && apt-get update     && apt-get install -y --no-install-recommends        curl        git     && rm -rf /var/lib/apt/lists/*

Step 2: Use fast local or regional mirrors

Replace slow default Ubuntu/Debian archive mirrors with faster, geographically closer mirrors (like Cloudflare or official fast mirrors) using sed:

# For Debian base images
RUN sed -i 's/deb.debian.org/debian.map.fastly.net/g' /etc/apt/sources.list.d/debian.sources     && apt-get update && apt-get install -y --no-install-recommends curl

# For Ubuntu base images
RUN sed -i 's/archive.ubuntu.com/ports.ubuntu.com/g' /etc/apt/sources.list     && apt-get update && apt-get install -y --no-install-recommends curl

Step 3: Enable BuildKit APT Caching (Recommended)

Docker BuildKit supports persistent package cache mounts across builds using --mount=type=cache. This retains downloaded APT packages across build runs without baking package lists into the final image size:

# syntax=docker/dockerfile:1
FROM ubuntu:22.04

# Enable persistent caching for apt archives and lists
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked     --mount=type=cache,target=/var/lib/apt,sharing=locked     apt-get update && apt-get install -y --no-install-recommends     build-essential     curl     git

Step 4: Optimize Dockerfile layer order

Ensure system dependency installations are placed at the very top of your Dockerfile, before copying application source code. This ensures apt-get update is cached indefinitely until you alter the actual dependency list:

FROM node:20-slim

WORKDIR /app

# Install system dependencies FIRST (cached layer)
RUN apt-get update && apt-get install -y --no-install-recommends     python3     make     g++     && rm -rf /var/lib/apt/lists/*

# Copy application files LAST to prevent invalidating the apt layer cache
COPY package*.json ./
RUN npm ci
COPY . .

Still Not Working?

If apt-get update continues to hang indefinitely during Docker builds on corporate networks or local developer machines, your Docker engine network MTU (Maximum Transmission Unit) setting may be mismatched with host interface limits.

Set the network MTU to 1460 or 1400 in /etc/docker/daemon.json to fix packet fragmentation issue on network interfaces:

{
  "mtu": 1400
}

Restart Docker and rebuild your image:

sudo systemctl restart docker
docker build --no-cache -t my-app .