Provision an apt-cacher-ng caching proxy inside a Debian 13 LXC container on Proxmox VE, end to end. One playbook run creates the container over the Proxmox API, installs and configures the proxy, and applies an nftables firewall. A second, optional playbook points apt clients at it.
A caching proxy stores each package the first time any host fetches it and
serves it from local disk afterwards, so repeated apt update / apt upgrade
across a fleet hit the LAN instead of the internet.
On the proxy container (apt_proxies group):
| Component | Purpose |
|---|---|
| apt-cacher-ng | Caching proxy on port 3142; report UI at /acng-report.html (basic auth); daily cache expiry |
| nftables | Default-drop firewall; only SSH, 3142, and (optionally) mDNS open to the configured source networks |
| chrony, ca-certificates, curl, dnsutils | Base packages |
| avahi-daemon (optional) | Advertises _apt_proxy._tcp for zero-config discovery by same-subnet clients |
On apt clients (apt_clients group, opt-in): a single
/etc/apt/apt.conf.d/00aptproxy drop-in pointing apt at the proxy.
| Role | Does |
|---|---|
proxmox_lxc |
Creates and starts the LXC over the API; idempotent on VMID, never destroys |
os_base |
Hostname, /etc/hosts, base packages |
apt_cacher_ng |
Installs and configures apt-cacher-ng (+ optional avahi) |
host_firewall |
nftables ruleset |
apt_proxy_client |
Points apt clients at the proxy |
ansible-galaxy collection install -r requirements.ymlpveam update
pveam download local debian-13-standard_13.1-2_amd64.tar.zstDatacenter → Permissions → API Tokens. Grant the token container create/configure rights on the target node and storage. Note the user, token id, and secret.
The public half is injected into the container; keys are gitignored, so generate your own:
ssh-keygen -t ed25519 -f ~/.ssh/apt-cache-deploy -N "" -C "apt-cache-deploy"
cp ~/.ssh/apt-cache-deploy.pub files/ssh/deploy.pubEdit inventory/group_vars/all/vault.yml (a plaintext stub) with real values,
then encrypt it:
vault_pve_api_token_secret: "<proxmox api token secret>"
vault_acng_admin_password: "<password for the report UI>"ansible-vault encrypt inventory/group_vars/all/vault.ymlIn inventory/proxmox.yml set the Proxmox API host/node/user/token id, the
container VMID, pve_ip/gateway/searchdomain, and firewall_allowed_sources.
ansible-playbook -i inventory/proxmox.yml site.yml --ask-vault-passRe-runs are idempotent; an existing VMID is left untouched. Teardown is manual:
pct destroy <vmid> on the node.
Add hosts to the apt_clients group with apt_proxy_host set, then:
ansible-playbook -i inventory/proxmox.yml playbooks/configure-clients.yml --ask-vault-passOr configure one host by hand:
echo 'Acquire::http::Proxy "http://<proxy-ip>:3142";' | sudo tee /etc/apt/apt.conf.d/00aptproxyPlain-http repos (modern Debian's deb.debian.org) are cached transparently.
For an HTTPS repo, add a remap in inventory/group_vars/all/main.yml:
apt_proxy_https_remaps:
- { key: docker-ce, upstream: "https://download.docker.com" }then point the client at http://<proxy>:3142/docker-ce/... instead of the
upstream URL.
With acng_avahi_enabled: true the proxy advertises _apt_proxy._tcp. Install
auto-apt-proxy on a client and it uses the cache automatically, no static
config. mDNS is link-local — it does not cross subnets; for cross-subnet managed
hosts use the apt_proxy_client role.
systemctl is-active apt-cacher-ng
curl -fsS -u admin:<password> http://<proxy>:3142/acng-report.html | head
nft list table inet aptproxyvagrant up
ansible-playbook -i inventory/vagrant.yml playbooks/provision-proxy.yml
ansible-playbook -i inventory/vagrant.yml playbooks/provision-proxy.yml # second run: changed=0
vagrant destroy -f- Ansible core ≥ 2.16 with the
community.proxmoxandcommunity.generalcollections. - Proxmox VE node with an API token and the Debian 13 LXC template downloaded.
- Run from the repo root. If your shell exports
ANSIBLE_ROLES_PATHorANSIBLE_COLLECTIONS_PATH, unset them soansible.cfgtakes effect.