Today I’m setting up a simple nginx proxy, so I can store updates used by my many Linux systems. Most of them run a derivative of Debian, so this guide focuses mostly on caching apt repositories (Debian, Ubuntu, Proxmox, and more), but the same approach should work with any distro.

Install nginx

I’m using a Debian 12 (Bookworm) unprivilaged LXC container, but this is basic nginx which should be in every distro ever. I pointed my local DNS address deb.palnet.net (using a DNS override in Unbound on OPNsense, in my case) to the IP of the container.

So:

#Ensure system is up to date
apt update
apt upgrade -y
#Install nginx
apt install nginx -y
#Set it to autostart
systemctl enable nginx
#Remove defaults in sites-enabled
cd /etc/nginx
rm sites-enabled/*

Now we’re ready to setup our cache!

Nginx Config

Here’s my nginx config (/etc/nginx/sites-available/debcache.conf):

# Global Cache settings
proxy_cache_path /var/debcache/cache levels=2:2 keys_zone=generic:500m inactive=3650d max_size=1000g min_free=5g loader_files=1000 loader_sleep=50ms loader_threshold=300ms use_temp_path=off;
# Log with cache status
log_format cachelog '$remote_addr [$time_local] "$request" $status "$http_user_agent" "$upstream_cache_status"';

# URI paths to avoid cache
# These paths will change to indicate new release contents
# All other .deb files can be cached nearly indefinitely, as the
# version number is coded into the file name.
map $request_uri $nocache {
    ~InRelease 1;
    ~Release 1;
    ~Packages 1;
}

# Deb Server
server {
    # IF you need legacy IP, enable this one
    #listen 80 reuseport;

    # Everyone needs modern IP
    listen [::]:80 reuseport;


    #Log settings
    access_log /var/debcache/access.log cachelog;
    error_log /var/debcache/error.log;


    # Cache Location
    slice 1m;
    proxy_cache generic;
    proxy_ignore_headers Expires Cache-Control;
    proxy_cache_valid 200 206 3650d;
    proxy_cache_valid 301 302 0;
    proxy_set_header  Range $slice_range;
    proxy_cache_lock on;
    proxy_cache_lock_age 2m;
    proxy_cache_lock_timeout 1h;
    proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
    proxy_cache_revalidate on;
    #Nocache for those entries
    proxy_cache_bypass $nocache;
    proxy_no_cache $nocache;
    # 1G max file
    proxy_max_temp_file_size 1024m;
    # Cache key
    proxy_cache_key      $http_host$uri$slice_range;
    # Upstream Configuration
    proxy_next_upstream error timeout http_404;
    # Cache status
    add_header X-Cache-Status $upstream_cache_status;
    proxy_redirect off;
    proxy_ignore_client_abort on;
    # Upstream request headers
    proxy_ssl_server_name on;

    # Redirect Locations
    # Must include trailing slash!

    # Debian
    location /debian/ {
        proxy_pass http://deb.debian.org/debian/;
        proxy_set_header Host "deb.debian.org";
    }
    location /debsec/ {
        proxy_pass http://deb.debian.org/debian-security/;
        proxy_set_header Host "deb.debian.org";
    }

    # Ubuntu
    location /ubuntu/ {
        proxy_pass http://us.archive.ubuntu.com/ubuntu/;
        proxy_set_header Host "us.archive.ubuntu.com";
    }
    location /ubusec/ {
        proxy_pass http://security.ubuntu.com/ubuntu/;
        proxy_set_header Host "security.ubuntu.com";
    }

    # Kali
    location /kali/ {
        proxy_pass http://http.kali.org/kali/;
        proxy_set_header Host "http.kali.org";
    }

    # Proxmox (non-enterprise)
    location /proxmox/ {
        proxy_pass http://download.proxmox.com/debian/;
        proxy_set_header Host "download.proxmox.com";
    }

    # Caddy Server
    location /caddy/ {
        proxy_pass https://dl.cloudsmith.io/public/caddy/stable/deb/debian/;
        proxy_set_header Host "dl.cloudsmith.io";
    }

    # Nodesource NodeJS
    location /node/ {
        proxy_pass http://deb.nodesource.com/;
        proxy_set_header Host "deb.nodesource.com";
    }

    # Authelia
    location /authelia/ {
        proxy_pass https://apt.authelia.com/stable/debian/debian/;
        proxy_set_header Host "apt.authelia.com";
    }

    # Influx
    location /influx/ {
        proxy_pass http://repos.influxdata.com/debian/;
        proxy_set_header Host "repos.influxdata.com";
    }

    # Stats endpoint
    location = /nginx_status {
        stub_status;
    }

    # Static Files (conversion scripts)
    root /var/debcache/static/;
    autoindex on;
}

Once you are done creating the file, create a link to it in sites-enabled so it is loaded by nginx:

cd /etc/nginx
ln -s ../sites-available/debcache.conf sites-enabled/debcache.conf

And restart nginx (systemctl restart nginx)

Replacement Script

I went through all of my Debian systems (to see which repos they are using), and wrote a simple script which uses sed to replace everything to point to my cache. I chose this approach instead of setting up apt to use a proxy, since this replaces https requests with http requests (to the cache - the cache -> upstream is still using HTTPS). This avoids complications in having apt trust a self-signed proxy cert. The lack of TLS is not significant here since deb packages are GPG signed.

Anyway, here’s the script (put it in /var/debcache/static/rewrite.sh)

#!/bin/bash

# My mirror location
export mirror=http://deb.palnet.net

# Load OS release info into variables
. /etc/os-release

#Function to rewrite a single file
rewrite() {
    echo Rewriting $1
    #Debian repos
    sed -i s#http://deb.debian.org/debian#$mirror/debian#g $1
    sed -i s#http://ftp.us.debian.org/debian#$mirror/debian#g $1
    sed -i s#http://security.debian.org#$mirror/debsec#g $1
    sed -i s#http://deb.debian.org/debian-security#$mirror/debsec#g $1
    #Ubuntu repos
    sed -i s#http://us.archive.ubuntu.com/ubuntu#$mirror/ubuntu#g $1
    sed -i s#http://security.ubuntu.com/ubuntu#$mirror/ubusec#g $1
    #Kali repos
    sed -i s#http://http.kali.org/kali#$mirror/kali#g $1
    #Proxmox repos (this catches all of them)
    sed -i s#http://download.proxmox.com/debian#$mirror/proxmox#g $1
    #Caddy server
    sed -i s#https://dl.cloudsmith.io/public/caddy/stable/deb/debian#$mirror/caddy#g $1
    #NodeJS from Nodesource
    sed -i s#https://deb.nodesource.com#$mirror/node#g $1
    #Authelia
    sed -i s#https://apt.authelia.com/stable/debian/debian#$mirror/authelia#g $1
    #Influxdata
    sed -i s#https://repos.influxdata.com/debian#$mirror/influx#g $1
}

#Rewrite sources.list itself
rewrite '/etc/apt/sources.list'

#Rewrite everything in sources.list.d
export -f rewrite
find '/etc/apt/sources.list.d/' -name "*.list" -type f -exec bash -c 'rewrite "$0"' {} \;

# If this system is using the 'new style' deb mirrors file (/etc/apt/sources.list.d/debian.sources)
# Replace it with equivalent sources.list entries
if test -f "/etc/apt/sources.list.d/debian.sources"; then
    echo "Rewriting /etc/apt/sources.list.d/debian.sources to sources.list format"
    echo "deb $mirror/debian $VERSION_CODENAME main contrib" >> /etc/apt/sources.list
    echo "deb-src $mirror/debian $VERSION_CODENAME main contrib" >> /etc/apt/sources.list
    echo "deb $mirror/debian $VERSION_CODENAME-updates main contrib" >> /etc/apt/sources.list
    echo "deb-src $mirror/debian $VERSION_CODENAME-updates main contrib" >> /etc/apt/sources.list
    echo "deb $mirror/debsec $VERSION_CODENAME-security main contrib" >> /etc/apt/sources.list
    echo "deb-src $mirror/debsec $VERSION_CODENAME-security main contrib" >> /etc/apt/sources.list
    rm /etc/apt/sources.list.d/debian.sources
fi

Then, on any system you have, you can run curl deb.palnet.net/rewrite.sh | bash and it will replace all of the deb references it finds. If you don’t have curl, you can do the same thing with wget -q -O - deb.palnet.net/rewrite.sh | bash (you may need to sudo bash depending on your current privilege)

Upgrade Distribution

Now that I have this place to put deb upgrade scripts, I wrote another one to replace bookworm with trixie to help upgrade deb systems easily.

This one goes in /var/debcache/static/bookworm2trixie.sh

#!/bin/bash
#Only do sources.list
sed -i 's/bookworm/trixie/g' /etc/apt/sources.list

and you can again get to it at deb.palnet.net/bookworm2trixie.sh and pipe it into bash.

Monitoring

You can access the logs of your cache from the debcache system, to see if you are getting hits or misses. The first system to update will miss everything, but after that the hit rate should be decent.

tail -f /var/debcache/access.log
#Or for the error file
tail -f /var/debcache/error.log