GuidesDevelopers and hostingLinux package cache

Cache Linux packages for your own servers

One server that keeps a copy of the packages your other servers download, for Debian and Ubuntu with apt-cacher-ng and for Arch, CachyOS and EndeavourOS with pacoloco, over HTTPS and only for your own servers.

Tested on apt-cacher-ng 3.7 and pacoloco 1.9 on Debian 13 (trixie) on a Melonslab server Updated September 25, 2026

Recommended server for this guide

VC-S Micro · 2 vCPU · 8 GB Memory · 250 GB Storage

Month to month, no lock-in 7-day money-back guarantee

€7.99/mo

Deploy now
On this page

What you will set up

When several servers install the same updates, each of them downloads the same packages from the internet. A package cache downloads each package once and hands out its copy after that. Unlike a full mirror, it only keeps what your servers actually install, so a few gigabytes are usually enough, and there is nothing to sync.

  • apt-cacher-ng caches for Debian and Ubuntu.
  • pacoloco caches for Arch Linux, CachyOS and EndeavourOS.

Both sit behind Caddy from the Podman guide, which gives them HTTPS and only lets your own servers in. Set up either or both.

Every step below was run on a Melonslab server with Debian 13. Debian 13 and Ubuntu 24.04 installed packages through apt-cacher-ng, and Arch Linux installed packages from its own, CachyOS's and EndeavourOS's repositories through pacoloco. The second installs came from the cache, and requests from any other address were refused.

Before you start

You need:

  • a server set up as in the Podman guide, with Caddy running;
  • a name for each cache, such as apt-cache.example.com and pacman-cache.example.com, with A and AAAA records pointing at the server;
  • the IPv4 and IPv6 addresses of the servers that will use the cache. The examples use 198.51.100.20 and 2001:db8:2::20.

List both addresses of every server: a server that has IPv6 connects over IPv6, and is refused if only its IPv4 address is on the list.

Debian and Ubuntu: apt-cacher-ng

1. Install it

As root:

apt update
apt install -y apt-cacher-ng

If apt asks whether to allow HTTP tunnels, keep the answer No. Debian's package runs apt-cacher-ng as its own user and keeps the cache in /var/cache/apt-cacher-ng.

2. Listen on the server only

Out of the box it listens on every address, which would make it a cache for anyone on the internet. Create /etc/apt-cacher-ng/local.conf:

# Only Caddy, on this server, can reach the cache.
BindAddress: 127.0.0.1 ::1
systemctl restart apt-cacher-ng

3. Put Caddy in front

Switch to Caddy's user with machinectl shell caddy@, and add this block to the end of ~/Caddyfile, with your own servers' addresses:

apt-cache.example.com {
    @servers remote_ip 198.51.100.20 2001:db8:2::20
    handle @servers {
        reverse_proxy 127.0.0.1:3142
    }
    respond 403
}

Restart Caddy with systemctl --user restart caddy. Caddy sees each visitor's real address, so the servers on the list get the cache and everyone else gets "403 Forbidden".

4. Point your servers at it

On each server that uses the cache, as root:

sed -i 's#http://#https://apt-cache.example.com/#' /etc/apt/sources.list
apt update

That turns a line such as deb http://ftp.debian.org/debian trixie main into deb https://apt-cache.example.com/ftp.debian.org/debian trixie main: apt asks the cache, and the cache fetches from the same place as before. Servers set up with the newer format keep their sources in /etc/apt/sources.list.d/debian.sources, or ubuntu.sources on Ubuntu; run the same sed on that file instead.

apt still checks the signature on every package, so the cache cannot change what your servers install.

Arch, CachyOS and EndeavourOS: pacoloco

1. Create the user

As root:

useradd -m -s /bin/bash pacoloco
loginctl enable-linger pacoloco
machinectl shell pacoloco@

2. Describe the cache

Create ~/pacoloco.yaml, with the repositories you use:

cache_dir: /var/cache/pacoloco
# Remove packages nobody has asked for in 30 days
purge_files_after: 2592000
repos:
  archlinux:
    urls:
      - https://mirror.accum.se/mirror/archlinux
  endeavouros:
    urls:
      - https://mirror.alpix.eu/endeavouros/repo
  cachyos:
    urls:
      - https://mirror.zyner.org/cachyos/repo

Without purge_files_after, pacoloco never removes anything. Each repository entry names an upstream mirror. These are ones close to Sweden; any mirror from the distribution's own mirror list works.

Create ~/.config/containers/systemd/pacoloco.container, after mkdir -p ~/.config/containers/systemd:

[Unit]
Description=pacoloco, a package cache for pacman

[Container]
ContainerName=pacoloco
Image=ghcr.io/anatol/pacoloco:1.9
Volume=%h/pacoloco.yaml:/etc/pacoloco.yaml:ro
# :U gives the volume to the user pacoloco runs as inside the container
Volume=pacoloco-cache:/var/cache/pacoloco:U
# Only Caddy, on this server, can reach it: the port is not open to the internet.
PublishPort=127.0.0.1:8087:9129
AutoUpdate=registry

[Service]
Restart=always

[Install]
WantedBy=default.target

The 1.9 tag is pacoloco's current release. Its latest tag follows unreleased code, so move to a newer release by changing the tag yourself.

systemctl --user daemon-reload
systemctl --user start pacoloco
systemctl --user enable --now podman-auto-update.timer

3. Put Caddy in front

As caddy, add this block to ~/Caddyfile, with your own servers' addresses, and restart Caddy:

pacman-cache.example.com {
    @servers remote_ip 198.51.100.20 2001:db8:2::20
    handle @servers {
        reverse_proxy 127.0.0.1:8087
    }
    respond 403
}

4. Point your machines at it

Put the cache first in each mirror list, above the other Server lines:

DistributionFileLine
Arch Linux/etc/pacman.d/mirrorlistServer = https://pacman-cache.example.com/repo/archlinux/$repo/os/$arch
CachyOS/etc/pacman.d/cachyos-mirrorlistServer = https://pacman-cache.example.com/repo/cachyos/$arch/$repo
EndeavourOS/etc/pacman.d/endeavouros-mirrorlistServer = https://pacman-cache.example.com/repo/endeavouros/$repo/$arch

CachyOS's -v3 and -v4 mirror lists use $arch_v3 or $arch_v4 in place of $arch; add the same line there with that change. pacoloco does not check package signatures itself, but pacman does, so the cache cannot change what you install.

Troubleshooting

apt or pacman gets "403 Forbidden". The machine's address is not on the list in the Caddy block. Check both of its addresses with curl -4 ifconfig.co and curl -6 ifconfig.co on that machine.

The cache fills the disk. apt-cacher-ng cleans out packages that are no longer in any repository every night. pacoloco removes packages nobody has asked for in 30 days, as set with purge_files_after; lower it if the disk is small.

Alpine or Rocky Linux. Neither project documents a package cache like these, so this guide does not cover them.

Run it on your own server

VC-S Micro

€7.99/mo

vCPU
2
Memory
8 GB
Storage
250 GB
Transfer
10 TB
Standard
HDD · RAID 10
  • Full root access
  • Native /64 IPv6
  • RAID-protected storage
  • Malmö, Sweden
  • Month to month, no lock-in
  • 7-day money-back guarantee
All guides