What you will set up
A mirror is a full copy of a distribution's packages that stays in step with the original and serves them to others. A public mirror helps the distribution and everyone near you; a private one serves only your own machines.
Here, a user of its own called mirror:
- syncs with rsync on a timer, the way each distribution asks mirrors to,
- runs a small web server container that serves the files, behind Caddy from the Podman guide,
- over both plain HTTP and HTTPS, as distributions expect of mirrors.
For your own servers only, a package cache is usually the better choice: it keeps only what you install. Arch Linux asks you to consider one before setting up a private Arch mirror.
Every step below was run on a Melonslab server with Debian 13. The EndeavourOS repository was synced completely and pacman on Arch Linux installed from it. Arch Linux's own sync script was set up and its update check run, and the sync it would make was measured; a full copy did not fit on the test server's disk. Both HTTP and HTTPS served the files without redirecting.
Which distributions fit
| Distribution | Size | Plan |
|---|---|---|
| Arch Linux, as its own script syncs it | 285 GB | Standard with 500 GB or more |
| EndeavourOS, packages only | 0.25 GB | any |
| EndeavourOS, with installer images | 93 GB | any Standard plan |
| CachyOS | 197 GB | Standard with 500 GB or more |
| Debian, amd64 with sources | about 1,450 GB | more storage: ask our sales team |
| Ubuntu | about 3,600 GB | more storage: ask our sales team |
| Alpine Linux | about 4,800 GB | more storage: ask our sales team |
| Rocky Linux | about 7,400 GB | more storage: ask our sales team |
We measured the sizes on 2026-09-25; Debian's, Ubuntu's and Alpine's come from the projects' own figures. They grow over time, so leave room. The last four need more storage than our Standard plans have: our sales team can set that up, and for your own servers only, the package cache needs a few gigabytes instead. This guide's sync steps cover the Arch Linux family.
Before you start
You need:
- a server set up as in the Podman guide, with Caddy running, and disk space from the table above;
- a name for the mirror, such as
mirror.example.com, with an A record and an AAAA record pointing at your server.
1. Install rsync and curl
As root:
apt update
apt install -y rsync curl
Arch Linux's sync script uses curl to check for changes before it runs rsync.
2. Create the user
useradd -m -s /bin/bash mirror
loginctl enable-linger mirror
machinectl shell mirror@
3. Serve the files
mkdir -p ~/mirror ~/bin ~/.config/containers/systemd ~/.config/systemd/user
Create ~/.config/containers/systemd/mirror-web.container:
[Unit]
Description=File server for the mirror
[Container]
ContainerName=mirror-web
Image=docker.io/library/caddy:2
Volume=%h/mirror:/srv:ro
# Only Caddy, on this server, can reach it: the port is not open to the internet.
PublishPort=127.0.0.1:8086:8080
Exec=caddy file-server --root /srv --browse --listen :8080
AutoUpdate=registry
[Service]
Restart=always
[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user start mirror-web
systemctl --user enable --now podman-auto-update.timer
The files stay in the mirror user's home, which no other user can read, so this user serves them itself, read-only, with directory listings.
4. Put Caddy in front
Go back to root with exit, switch to machinectl shell caddy@, and add this block to the end of ~/Caddyfile:
# Both HTTP and HTTPS, without redirecting: distributions expect plain HTTP on mirrors.
http://mirror.example.com, https://mirror.example.com {
reverse_proxy 127.0.0.1:8086
}
Restart Caddy with systemctl --user restart caddy. Naming both http:// and https:// stops Caddy from sending plain HTTP visitors on to HTTPS. Packages are signed, so pacman can trust them over either.
5. Sync a distribution
Go back to the mirror user with exit and machinectl shell mirror@. Then set up one or more of the following.
Arch Linux
Arch Linux publishes the script its mirrors use. It first checks a small lastupdate file over HTTPS, and only runs rsync when that has changed. Download it, and set it up to sync from accum.se in Umeå, one of Arch's Tier 1 mirrors:
curl -o ~/bin/syncrepo-archlinux https://gitlab.archlinux.org/archlinux/infrastructure/-/raw/main/roles/syncrepo/files/syncrepo-template.sh
sed -i \
-e 's|^target=.*|target="$HOME/mirror/archlinux"|' \
-e 's|^lock=.*|lock="$HOME/.syncrepo-archlinux.lock"|' \
-e 's|^source_url=.*|source_url="rsync://mirror.accum.se/mirror/archlinux/"|' \
-e 's|^tls=.*|tls=0|' \
-e 's|^lastupdate_url=.*|lastupdate_url="https://mirror.accum.se/mirror/archlinux/lastupdate"|' \
~/bin/syncrepo-archlinux
chmod +x ~/bin/syncrepo-archlinux
tls=0 is needed because accum.se does not offer rsync over TLS. Create ~/.config/systemd/user/sync-archlinux.service:
[Unit]
Description=Sync the Arch Linux mirror
[Service]
Type=oneshot
ExecStart=%h/bin/syncrepo-archlinux
And ~/.config/systemd/user/sync-archlinux.timer:
[Unit]
Description=Sync the Arch Linux mirror every hour, on a fixed random minute
[Timer]
OnCalendar=hourly
RandomizedDelaySec=1h
FixedRandomDelay=true
[Install]
WantedBy=timers.target
Run the first sync by hand, which shows its progress and copies about 285 GB, then switch on the timer:
~/bin/syncrepo-archlinux
systemctl --user daemon-reload
systemctl --user enable --now sync-archlinux.timer
From then on the timer checks every hour, always on the same randomly chosen minute, as Arch asks of its mirrors. When nothing has changed, a run takes a few seconds.
EndeavourOS
Create ~/bin/sync-endeavouros:
#!/bin/sh
# Sync EndeavourOS. flock stops a run from starting while the last one is still going.
exec flock -n "$HOME/.sync-endeavouros.lock" \
rsync -rlptH --safe-links --delete-delay --delay-updates --timeout=600 \
rsync://alpix.eu.rsync.endeavouros.com/endeavouros/ "$HOME/mirror/endeavouros/"
That syncs the packages and the installer images, 93 GB. For the packages only, add repo/ to both paths. Make it executable with chmod +x ~/bin/sync-endeavouros, and create a service and timer as for Arch Linux, named sync-endeavouros, with ExecStart=%h/bin/sync-endeavouros. Start the service once for the first sync, then enable the timer.
--delay-updates and --delete-delay keep the mirror consistent while it syncs: new files appear together at the end, and old ones are removed after that.
CachyOS
CachyOS allows mirrors, but does not publish where to sync from. Write to admin@cachyos.org, who will give you an rsync source. Then copy the EndeavourOS script to ~/bin/sync-cachyos, put their source in place of the EndeavourOS one and $HOME/mirror/cachyos/ as the target, and add a service and timer the same way. CachyOS mirrors keep the packages under repo/, as the line in step 6 expects; check that yours does after the first sync.
6. Check it
Open https://mirror.example.com/: the directory listing shows each distribution you sync. To use the mirror from pacman, put it first in the mirror list:
| Distribution | File | Line |
|---|---|---|
| Arch Linux | /etc/pacman.d/mirrorlist | Server = https://mirror.example.com/archlinux/$repo/os/$arch |
| EndeavourOS | /etc/pacman.d/endeavouros-mirrorlist | Server = https://mirror.example.com/endeavouros/repo/$repo/$arch |
| CachyOS | /etc/pacman.d/cachyos-mirrorlist | Server = https://mirror.example.com/cachyos/repo/$arch/$repo |
7. Keep it private, or make it public
Private. To let only your own machines in, add an address check to the Caddy block, as in the package cache guide.
Public. Each distribution lists public mirrors itself, after checking them:
- Arch Linux: open a feature request at gitlab.archlinux.org/archlinux/arch-mirrors (you need to log in), naming the Tier 1 mirror you sync from. Its mirrors sync all of the upstream mirror, at most once an hour and at least once a day, on a random minute, which the steps above do.
- EndeavourOS: email alpix at endeavouros.com. It asks for at least 100 Mbit/s, IPv4, a sync at least every 3 hours, and no Cloudflare in front, and watches a new mirror for a week or two before listing it.
- CachyOS: ask when you get your rsync source.
Troubleshooting
The Arch Linux sync fails to connect on port 874. tls is still 1: accum.se only offers plain rsync. Check the sed step.
A sync does not start. The last one is still running: the lock file makes a second run exit straight away. systemctl --user status sync-archlinux shows what it is doing.
The disk fills up. Compare du -sh ~/mirror/* with the table above, which grows over time.
Running a public mirror?
A public mirror serves steady, heavy traffic: a popular one sends many terabytes a month. Talk to our sales team before you open one to the public, so we can plan the capacity with you.