Skip to content

๐Ÿ“ฆ reprepro

reprepro is used as a local repository for deb packages.

Some apps, like SOPS, release deb files, but are not a part of the normal repository. Hosting them locally, allows me to download the package once and then easily update on all other containers.

๐Ÿ“ Raspberry Pi Support

The raspi repository is used to support ARMv6 devices, such as the Raspberry Pi 1 and Raspberry Pi Zero W. These armv6 raspberry pi computers use the https://deb.l.nicholaswilde.io/raspi/dists/trixie/ repository.

The /etc/apt/sources.list.d/reprepro.sources file for these computers is:

Types: deb
URIs: https://deb.l.nicholaswilde.io/raspi
Suites: trixie
Components: main
Signed-By: /etc/apt/keyrings/reprepro.gpg

๐Ÿ›  Installation

apt install reprepro apache2 gpg

โš™ Config

๐Ÿชถ Apache

/etc/apache2/apache2.conf

echo "ServerName localhost" | tee -a /etc/apache2/apache2.conf
ServerName localhost

/etc/apache2/conf-availabe/repos.conf

cat <<EOF > /etc/apache2/conf-availabe/repos.conf 
# /etc/apache2/conf.available/repos.conf
# Apache HTTP Server 2.4

Alias /repos/apt/debian /srv/reprepro/debian

<Directory /srv/reprepro/ >
        # We want the user to be able to browse the directory manually
        Options Indexes FollowSymLinks Multiviews
        Require all granted
</Directory>

# This syntax supports several repositories, e.g. one for Debian, one for Ubuntu.
# Replace * with debian, if you intend to support one distribution only.
<Directory "/srv/reprepro/*/db/">
        Require all denied
</Directory>

<Directory "/srv/reprepro/*/conf/">
        Require all denied
</Directory>

<Directory "/srv/reprepro/*/incoming/">
        Require all denied
</Directory>

EOF
curl -Lo /etc/apache2/conf-availabe/repos.conf https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/apache2/conf-available/repos.conf
# /etc/apache2/conf.available/repos.conf
# Apache HTTP Server 2.4

Alias /repos/apt/debian /srv/reprepro/debian

<Directory /srv/reprepro/ >
        # We want the user to be able to browse the directory manually
        Options Indexes FollowSymLinks Multiviews
        Require all granted
</Directory>

# This syntax supports several repositories, e.g. one for Debian, one for Ubuntu.
# Replace * with debian, if you intend to support one distribution only.
<Directory "/srv/reprepro/*/db/">
        Require all denied
</Directory>

<Directory "/srv/reprepro/*/conf/">
        Require all denied
</Directory>

<Directory "/srv/reprepro/*/incoming/">
        Require all denied
</Directory>

Enable and test

(
    a2enconf repos && \
    apache2ctl configtest && \
    service apache2 restart
)

โš™ Config

๐Ÿ“ฆ Repository

Make directories

shell ( [ -d /srv/reprepro/debian/conf ] || mkdir -p /srv/reprepro/debian/conf [ -d /srv/reprepro/ubuntu/conf ] || mkdir -p /srv/reprepro/ubuntu/conf )

Generate new gpg keys

gpg --full-generate-key
gpg --list-keys  
 pub  2048R/489CD644 2014-07-15  
 uid         Your Name <[email protected]>  
 sub  2048R/870B8E2D 2014-07-15

Get short fingerprint

gpg -k [email protected] | sed -n '2p'| sed 's/ //g' | tail -c 9

short fingerprint

089C9FAF

Export public gpg key

gpg --export-options export-minimal -a --export 089C9FAF | sudo tee /srv/reprepro/public.gpg.key

/srv/reprepo/<dist>/conf/distributions

(
  cat <<EOF > /srv/reprepo/debian/conf/distributions
  Origin: Debian  
  Label: Bookworm apt repository  
  Codename: bookworm
  Architectures: amd64 arm64 armhf armel
  Components: main  
  Description: Apt repository for Debian stable - Bookworm  
  DebOverride: override.bookworm
  DscOverride: override.bookworm
  SignWith: 089C9FAF 

  Origin: Debian  
  Label: Bullseye apt repository
  Codename: bullseye
  Architectures: amd64 arm64 armhf armel
  Components: main  
  Description: Apt repository for Debian stable - Bullseye  
  DebOverride: override.bullseye
  DscOverride: override.bullseye
  SignWith: 089C9FAF

  Origin: Debian
  Label: Trixie apt repository
  Codename: trixie
  Architectures: amd64 arm64 armhf armel
  Components: main
  Description: Apt repository for Debian stable - Trixie
  DebOverride: override.trixie
  DscOverride: override.trixie
  SignWith: 089C9FAF

  Origin: Debian
  Label: Raspberry Pi apt repository
  Codename: raspi
  Architectures: armhf
  Components: main
  Description: Apt repository for Raspberry Pi 1/Zero (ARMv6)
  DebOverride: override.raspi
  DscOverride: override.raspi
  SignWith: 089C9FAF

  EOF
  cat <<EOF > /srv/reprepo/ubuntu/conf/distributions
  Origin: Ubuntu
  Label: Questing apt repository
  Codename: questing
  Architectures: amd64 arm64 armhf armel
  Components: main  
  Description: Apt repository for Ubuntu stable - Questing
  DebOverride: override.questing
  DscOverride: override.questing
  SignWith: 089C9FAF 

  Origin: Ubuntu
  Label: Plucky apt repository
  Codename: plucky
  Architectures: amd64 arm64 armhf armel
  Components: main  
  Description: Apt repository for Ubuntu stable - Plucky
  DebOverride: override.plucky
  DscOverride: override.plucky
  SignWith: 089C9FAF 

  Origin: Ubuntu
  Label: Oracular apt repository
  Codename: oracular
  Architectures: amd64 arm64 armhf armel
  Components: main  
  Description: Apt repository for Ubuntu stable - Oracular
  DebOverride: override.oracular
  DscOverride: override.oracular
  SignWith: 089C9FAF 

  Origin: Ubuntu
  Label: Noble apt repository
  Codename: noble
  Architectures: amd64 arm64 armhf armel
  Components: main  
  Description: Apt repository for Ubuntu stable - Noble
  DebOverride: override.noble
  DscOverride: override.noble
  SignWith: 089C9FAF 

  Origin: Ubuntu
  Label: Jammy apt repository
  Codename: jammy
  Architectures: amd64 arm64 armhf armel
  Components: main  
  Description: Apt repository for Ubuntu stable - Jammy
  DebOverride: override.jammy
  DscOverride: override.jammy
  SignWith: 089C9FAF 

  EOF
  cat <<EOF > /srv/reprepo/raspi/conf/distributions
  Origin: Debian
  Label: Raspberry Pi apt repository
  Codename: trixie
  Architectures: armhf
  Components: main
  Description: Apt repository for Raspberry Pi 1/Zero (ARMv6)
  DebOverride: override.trixie
  DscOverride: override.trixie
  SignWith: 089C9FAF

  Origin: Debian
  Label: Raspberry Pi apt repository
  Codename: bookworm
  Architectures: armhf
  Components: main
  Description: Apt repository for Raspberry Pi 1/Zero (ARMv6)
  DebOverride: override.bookworm
  DscOverride: override.bookworm
  SignWith: 089C9FAF

  EOF

)
(
  ln -s /root/git/nicholaswilde/homelab/pve/reprepro/debian/conf/distributions /srv/reprepro/debian/conf/distributions
  ln -s /root/git/nicholaswilde/homelab/pve/reprepro/ubuntu/conf/distributions /srv/reprepro/ubuntu/conf/distributions
  ln -s /root/git/nicholaswilde/homelab/pve/reprepro/raspi/conf/distributions /srv/reprepro/raspi/conf/distributions
)
(
  curl -Lo /srv/reprepro/debian/conf/distributions https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/debian/conf/distributions
  curl -Lo /srv/reprepro/ubuntu/conf/distributions https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/ubuntu/conf/distributions
  curl -Lo /srv/reprepro/raspi/conf/distributions https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/raspi/conf/distributions
)
Origin: Debian  
Label: Bookworm apt repository  
Codename: bookworm
Architectures: amd64 arm64 armhf armel
Components: main  
Description: Apt repository for Debian stable - Bookworm  
DebOverride: override.bookworm
DscOverride: override.bookworm
SignWith: 089C9FAF 

Origin: Debian  
Label: Bullseye apt repository
Codename: bullseye
Architectures: amd64 arm64 armhf armel
Components: main  
Description: Apt repository for Debian stable - Bullseye  
DebOverride: override.bullseye
DscOverride: override.bullseye
SignWith: 089C9FAF

Origin: Debian
Label: Trixie apt repository
Codename: trixie
Architectures: amd64 arm64 armhf armel
Components: main
Description: Apt repository for Debian stable - Trixie
DebOverride: override.trixie
DscOverride: override.trixie
SignWith: 089C9FAF

Origin: Debian
Label: Raspberry Pi apt repository
Codename: raspi
Architectures: armhf
Components: main
Description: Apt repository for Raspberry Pi 1/Zero (ARMv6)
DebOverride: override.raspi
DscOverride: override.raspi
SignWith: 089C9FAF
Origin: Ubuntu
Label: Questing apt repository
Codename: questing
Architectures: amd64 arm64 armhf armel
Components: main  
Description: Apt repository for Ubuntu stable - Questing
DebOverride: override.questing
DscOverride: override.questing
SignWith: 089C9FAF 

Origin: Ubuntu
Label: Plucky apt repository
Codename: plucky
Architectures: amd64 arm64 armhf armel
Components: main  
Description: Apt repository for Ubuntu stable - Plucky
DebOverride: override.plucky
DscOverride: override.plucky
SignWith: 089C9FAF 

Origin: Ubuntu
Label: Oracular apt repository
Codename: oracular
Architectures: amd64 arm64 armhf armel
Components: main  
Description: Apt repository for Ubuntu stable - Oracular
DebOverride: override.oracular
DscOverride: override.oracular
SignWith: 089C9FAF 

Origin: Ubuntu
Label: Noble apt repository
Codename: noble
Architectures: amd64 arm64 armhf armel
Components: main  
Description: Apt repository for Ubuntu stable - Noble
DebOverride: override.noble
DscOverride: override.noble
SignWith: 089C9FAF 

Origin: Ubuntu
Label: Jammy apt repository
Codename: jammy
Architectures: amd64 arm64 armhf armel
Components: main  
Description: Apt repository for Ubuntu stable - Jammy
DebOverride: override.jammy
DscOverride: override.jammy
SignWith: 089C9FAF 
Origin: Debian
Label: Raspberry Pi apt repository
Codename: trixie
Architectures: armhf
Components: main
Description: Apt repository for Raspberry Pi 1/Zero (ARMv6)
DebOverride: override.trixie
DscOverride: override.trixie
SignWith: 089C9FAF

Origin: Debian
Label: Raspberry Pi apt repository
Codename: bookworm
Architectures: armhf
Components: main
Description: Apt repository for Raspberry Pi 1/Zero (ARMv6)
DebOverride: override.bookworm
DscOverride: override.bookworm
SignWith: 089C9FAF

/srv/reprepo/<dist>/conf/options

cat <<EOF > /srv/reprepo/debian/conf/options
verbose
basedir /srv/reprepro/debian
ask-passphrase
EOF
cat <<EOF > /srv/reprepo/ubuntu/conf/options
verbose
basedir /srv/reprepro/ubuntu
ask-passphrase
EOF
cat <<EOF > /srv/reprepo/ubuntu/conf/options
verbose
basedir /srv/reprepro/raspi
ask-passphrase
EOF
(
  curl -Lo /srv/reprepro/debian/conf/options https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/debian/conf/options
  curl -Lo /srv/reprepro/ubuntu/conf/options https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/ubuntu/conf/options
  curl -Lo /srv/reprepro/raspi/conf/options https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/raspi/conf/options
)
(
  ln -s /root/git/nicholaswilde/homelab/pve/reprepro/debian/conf/options /srv/reprepro/debian/conf/options
  ln -s /root/git/nicholaswilde/homelab/pve/reprepro/ubuntu/conf/options /srv/reprepro/ubuntu/conf/options
  ln -s /root/git/nicholaswilde/homelab/pve/reprepro/raspi/conf/options /srv/reprepro/raspi/conf/options
)
verbose
basedir /srv/reprepro/debian
ask-passphrase
verbose
basedir /srv/reprepro/ubuntu
ask-passphrase
verbose
basedir /srv/reprepro/raspi
ask-passphrase

/srv/reprepo/<dist>/conf/override.<codename>

task symlinks
(
  sudo curl -Lo /srv/reprepro/debian/conf/override.bullseye https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/debian/conf/override.bullseye
  sudo curl -Lo /srv/reprepro/debian/conf/override.bookworm https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/debian/conf/override.bookworm
  sudo curl -Lo /srv/reprepro/debian/conf/override.trixie https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/debian/conf/override.trixie
  sudo curl -Lo /srv/reprepro/debian/conf/override.raspi https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/debian/conf/override.raspi
  sudo curl -Lo /srv/reprepro/ubuntu/conf/override.questing https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/ubuntu/conf/override.questing
  sudo curl -Lo /srv/reprepro/ubuntu/conf/override.plucky https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/ubuntu/conf/override.plucky
  sudo curl -Lo /srv/reprepro/ubuntu/conf/override.oracular https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/ubuntu/conf/override.oracular
  sudo curl -Lo /srv/reprepro/ubuntu/conf/override.noble https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/ubuntu/conf/override.noble
  sudo curl -Lo /srv/reprepro/ubuntu/conf/override.jammy https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/ubuntu/conf/override.jammy
  sudo curl -Lo /srv/reprepro/raspi/conf/override.bookworm https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepr/raspi/conf/override.bookworm
  sudo curl -Lo /srv/reprepro/raspi/conf/override.trixie https://github.com/nicholaswilde/homelab/raw/refs/heads/main/pve/reprepro/raspi/conf/override.trixie

)
(
  sudo ln -fs /root/git/nicholaswilde/homelab/pve/reprepro/debian/conf/override.bullseye /srv/reprepro/debian/conf/override.bullseye
  sudo ln -fs /root/git/nicholaswilde/homelab/pve/reprepro/debian/conf/override.bookworm /srv/reprepro/debian/conf/override.bookworm
  sudo ln -fs /root/git/nicholaswilde/homelab/pve/reprepro/debian/conf/override.trixie /srv/reprepro/debian/conf/override.trixie
  sudo ln -fs /root/git/nicholaswilde/homelab/pve/reprepro/debian/conf/override.raspi /srv/reprepro/debian/conf/override.raspi
  sudo ln -fs /root/git/nicholaswilde/homelab/pve/reprepro/ubuntu/conf/override.questing /srv/reprepro/ubuntu/conf/override.questing
  sudo ln -fs /root/git/nicholaswilde/homelab/pve/reprepro/ubuntu/conf/override.plucky /srv/reprepro/ubuntu/conf/override.plucky
  sudo ln -fs /root/git/nicholaswilde/homelab/pve/reprepro/ubuntu/conf/override.oracular /srv/reprepro/ubuntu/conf/override.oracular
  sudo ln -fs /root/git/nicholaswilde/homelab/pve/reprepro/ubuntu/conf/override.noble /srv/reprepro/ubuntu/conf/override.noble
  sudo ln -fs /root/git/nicholaswilde/homelab/pve/reprepro/ubuntu/conf/override.jammy /srv/reprepro/ubuntu/conf/override.jammy
  sudo ln -fs /root/git/nicholaswilde/homelab/pve/reprepro/raspi/conf/override.bookworm /srv/reprepro/raspi/conf/override.bookworm
  sudo ln -fs /root/git/nicholaswilde/homelab/pve/reprepro/raspi/conf/override.trixie /srv/reprepro/raspi/conf/override.trixie
)
(
  sudo touch /srv/reprepro/debian/conf/override.bookworm
  sudo touch /srv/reprepro/debian/conf/override.bullseye
  sudo touch /srv/reprepro/debian/conf/override.trixie
  sudo touch /srv/reprepro/debian/conf/override.raspi
  sudo touch /srv/reprepro/ubuntu/conf/override.questing
  sudo touch /srv/reprepro/ubuntu/conf/override.plucky
  sudo touch /srv/reprepro/ubuntu/conf/override.oracular
  sudo touch /srv/reprepro/ubuntu/conf/override.noble
  sudo touch /srv/reprepro/ubuntu/conf/override.jammy
  sudo touch /srv/reprepro/raspi/conf/override.bookworm
  sudo touch /srv/reprepro/raspi/conf/override.trixie
)

๐Ÿ”‘ Environmental File

A .env file is used to set variables that are used with task and scripts.

homelab/pve/reprepro

task init
cp .env.tmpl .env

Edit the .env file with your preferred text editor.

.env
# Used by multiple scripts
GITHUB_TOKEN=
BASE_DIR=/srv/reprepro
ENABLE_NOTIFICATIONS="true"
DEBUG="false"
SERVICE_MODE="false"

# sync-check.sh
SYNC_APPS_GITHUB_REPOS=("dandavison/delta:git-delta" "go-task/task" "sharkdp/fd" "sharkdp/bat" "localsend/localsend" "BurntSushi/ripgrep" "muesli/duf" "charmbracelet/glow" "cli/cli:gh" "cloudflare/cloudflared" "nicholaswilde/sysmqttd")

# package-neovim.sh
PACKAGE_APPS=("BurntSushi/ripgrep:file_strip:rg:.*-\K[^-]+(?=-unknown-linux-gnu)" "eza-community/eza:all::(?<=_)[^-]*" "chmln/sd:file_strip::.*-\K[^-]+(?=-unknown-linux)" "aristocratos/btop:file_path:btop/bin/btop:(?<=btop-)[^-]+" "micro-editor/micro:file_strip:micro:linux-?\K[a-z0-9]+" "adnanh/webhook:file_strip:webhook:linux-\\K[^.]+")

# Mailrise notifications
MAILRISE_URL='smtp://smtp.l.nicholaswilde.io:8025'
MAILRISE_FROM='[email protected]'
MAILRISE_RCPT='[email protected]'

# upload-neovim.sh
REMOTE_IP=192.168.2.32
REMOTE_USER=root
REMOTE_PATH=/root/

# Builders Configuration
BUILDER_CACHE_MAX_AGE_DAYS=7

๐Ÿท Adding a New Release Codename

To add a new release codename to reprepro:

  1. Add a new override.<codename> file in /srv/reprepro/<dist>/conf.
  2. Add a new entry to /srv/reprepro/<dist>/conf/distributions file.

/srv/reprepro/<dist>/conf/distributions

Origin: Ubuntu
Label: Oracular apt repository
Codename: plucky
Architectures: amd64 arm64 armhf
Components: main
Description: Apt repository for Ubuntu stable - Plucky
DebOverride: override.plucky
DscOverride: override.plucky
SignWith: 089C9FAF

๐Ÿ“ Usage

๐Ÿ–ฅ Server

Add deb file to reprepro.

(
  sudo reprepro -b /srv/reprepro/debian -C main includedeb trixie sops_3.9.4_amd64.deb
  sudo reprepro -b /srv/reprepro/debian -C main includedeb bullseye sops_3.9.4_amd64.deb
  sudo reprepro -b /srv/reprepro/debian -C main includedeb trixie sops_3.9.4_amd64.deb
  sudo reprepro -b /srv/reprepro/raspi -C main includedeb trixie sops_3.9.4_armhf.deb
  sudo reprepro -b /srv/reprepro/ubuntu -C main includedeb questing sops_3.9.4_amd64.deb
  sudo reprepro -b /srv/reprepro/ubuntu -C main includedeb plucky sops_3.9.4_amd64.deb
  sudo reprepro -b /srv/reprepro/ubuntu -C main includedeb oracular sops_3.9.4_amd64.deb
  sudo reprepro -b /srv/reprepro/ubuntu -C main includedeb noble sops_3.9.4_amd64.deb
  sudo reprepro -b /srv/reprepro/ubuntu -C main includedeb jammy sops_3.9.4_amd64.deb
)

๐Ÿ’ป Client

Download gpg key

curl -fsSL http://deb.l.nicholaswilde.io/public.gpg.key | gpg --dearmor -o /etc/apt/keyrings/reprepro.gpg
curl -fsSL http://deb.l.nicholaswilde.io/public.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/reprepro.gpg

Add repo and install.

/etc/apt/sources.list.d/reprepro.sources

(
  source /etc/os-release && \
  echo "Types: deb
URIs: http://deb.l.nicholaswilde.io/${ID}
Suites: ${VERSION_CODENAME}
Components: main
Signed-By: /etc/apt/keyrings/reprepro.gpg" | tee /etc/apt/sources.list.d/reprepro.sources > /dev/null
  apt update && \
  apt install sops
)
(
  source /etc/os-release && \
  echo "Types: deb
URIs: http://deb.l.nicholaswilde.io/${ID}
Suites: ${VERSION_CODENAME}
Components: main
Signed-By: /etc/apt/keyrings/reprepro.gpg" | sudo tee /etc/apt/sources.list.d/reprepro.sources > /dev/null
  sudo apt update && \
  sudo apt install sops
)
Types: deb
URIs: http://deb.l.nicholaswilde.io/debian
Suites: bookworm
Components: main
Signed-By: /etc/apt/keyrings/reprepro.gpg
apt update && \
apt install sops
Types: deb
URIs: http://deb.l.nicholaswilde.io/debian
Suites: bullseye
Components: main
Signed-By: /etc/apt/keyrings/reprepro.gpg
apt update && \
apt install sops
Types: deb
URIs: http://deb.l.nicholaswilde.io/debian
Suites: trixie
Components: main
Signed-By: /etc/apt/keyrings/reprepro.gpg
apt update && \
apt install sops
Types: deb
URIs: https://deb.l.nicholaswilde.io/raspi
Suites: trixie
Components: main
Signed-By: /etc/apt/keyrings/reprepro.gpg
apt update && \
apt install sops

โ†” Copy all package from one codename to another from noble to questing.

sudo reprepro -b /srv/reprepro/ubuntu/ copymatched questing noble '*'

๐Ÿ“‹ List all package information

find /srv/reprepro/ubuntu/dists/noble -name 'Packages.gz' -exec zcat {} +

โ™ป Regenerate the Repository Index

This can fix BADSIG errors shown on remote hosts.

The badsig error means the Release.gpg file for that codename does not contain a valid signature for the Release file. The Release file itself contains a list of all other index files (like Packages.gz) and their checksums.

sudo reprepro -b /srv/reprepro/debian/ -V export noble

๐Ÿ“œ Scripts

Some scripts are provided to help with common tasks.

๐Ÿ“ฆ Update Reprepro

The script update-reprepro.sh is used to compare the latest released versions of the apps specified with the SYNC_APPS_GITHUB_REPOS and PACKAGE_APPS variables in the .env file to the local versions.

If out of date, the compressed archives specified in the PACKAGE_APPS variable are downloaded, packaged into deb files, and added to reprepro and deb files located in the SYNC_APPS_GITHUB_REPOS variable are downloaded and add to reprepro.

Automation

The update-reprepro-service.sh script can be triggered automatically using a webhook. This is useful for continuous integration, for example, triggering an update after a new package has been built and pushed to GitHub. I use ChangeDetection.io to monitor GitHub releases and trigger this webhook.

hooks.json

[
  {
    "id": "update-app",
    "execute-command": "/root/git/nicholaswilde/homelab/pve/reprepro/update-reprepro-service.sh",
    "command-working-directory": "/root/git/nicholaswilde/homelab/pve/reprepro",
    "pass-arguments-to-command": [
      {
        "source": "string",
        "name": "-s"
      }
    ],
    "response-message": "Updating reprepro..."
  }
]

reprepro-webhook.service

[Unit]
Description=reprepro Webhook Listener
After=network.target

[Service]
Type=simple
User=root
Environment=PATH=/home/root/.local/share/pnpm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/bin:/bin:/root/.local/bin
WorkingDirectory=/root/git/nicholaswilde/homelab/pve/reprepro
ExecStart=/usr/local/bin/webhook -hooks hooks.json -verbose -port 9000
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

homelab/pve/reprepro

task update-reprepro
sudo ./update-reprepro.sh
package-apps.sh

๐Ÿ“ฆ Package Neovim

The script package-neovim.sh is used to compare the latest released version of Neovim to the local version in reprepro.

If out of date, the compressed archive is downloaded, built, packaged into a deb file.

The reason this is separate from update-reprepro.sh is because dependencies need to get packaged along with the binary and an armhf version is not part of the release package.

There are three ways to build the Neovim package for different architectures:

  1. Docker: Use @pve/reprepro/docker/** on the localhost to build for multiple platforms.
  2. Ansible: Use @pve/reprepro/ansible/** if you have physical machines with different architectures.
  3. Manual Script: Log into each machine with a different architecture and run the @pve/reprepro/package-neovim.sh script.

Tip

To get multiple architectures of the deb file, the script may be run on different architecture platforms. For instance, I use my RPi2 to build the armv7l, RPi5 to build the arm64, and HP to build the amd64 version.

homelab/pve/reprepro

task package-neovim
sudo ./package-neovim.sh
package-neovim.sh

๐Ÿ“ฆ Package SOPS

The script package-sops.sh is used to compare the latest released version of SOPS to the local version in reprepro.

If out of date, the compressed archive is downloaded, built, packaged into a deb file.

The reason this is separate from update-reprepro.sh is because the sops repo doesn't offer an armhf version and so I manually build and package the armhf version and add it to reprepro.

Tip

To get multiple architectures of the deb file, the script may be run on different architecture platforms. For instance, I use my RPi2 to build the armhf version.

homelab/pve/reprepro

task package-sops
sudo ./package-sops.sh
package-sops.sh

๐Ÿ“ค Upload Deb Files

Once the neovim or sops deb files are built, they are copied to the current pve/reprepro folder. The upload-debs task can then be used to push the deb files to the reprepro LXC using scp.

The REMOTE_IP, REMOTE_USER, and REMOTE_PATH variables in the .env file are used to specify the reprepro LXC.

task upload-debs

๐Ÿ“ก Remote SSH Orchestration

To simplify remote management of the reprepro host directly from your local development workstation, we support remote task runners and a preconfigured SSH host alias.

SSH Host Configuration

To enable clean, passwordless, and non-interactive connections, add the following host profile to your local ~/.ssh/config file:

Host reprepro
  HostName 192.168.1.58
  User root
  StrictHostKeyChecking no
  UserKnownHostsFile /dev/null
  BatchMode yes

This configuration maps the root SSH user and automatically handles strict host verification bypass.

Remote Task Commands

The local Taskfile.yml includes dedicated tasks to orchestrate repository operations remotely on the reprepro server:

  • List Remote Packages:
    task remote:list
    
    Recursively queries and prints all packages currently registered under all Debian, Ubuntu, and Raspi distributions on the remote server.
  • Trigger Remote Sync:
    task remote:sync
    
    Triggers the master synchronization script (update-reprepro-service.sh) remotely to fetch latest versions from upstream and update the reprepro repository.
  • Import Remote ARMv6 Packages:
    task remote:add-raspi
    
    Triggers the add-raspi-debs.sh script remotely on the server to scan /root and register any newly compiled ARMv6 packages into the raspi distribution.

๐Ÿ”” Script Notifications

Some scripts can send notifications via Mailrise.

Set the MAILRISE_* variables and the ENABLE_NOTIFICATIONS variable in the .env file.

๐Ÿค– Automated Builder

The builder system in pve/reprepro/builders/ provides an automated way to build and sync packages using a Webhook listener.

Purpose

This system is mainly used when remote app releases do not include armv7 releases. It ensures the correct architecture is available for my Raspberry Pi 2 and Raspberry Pi Zero W.

โš“ Webhook Listener

A systemd service is used to listen for build requests.

reprepro-webhook.service

[Unit]
Description=Reprepro Builder Webhook Listener
After=network.target

[Service]
Type=simple
User=nicholas
Group=nicholas
WorkingDirectory=/home/nicholas/git/nicholaswilde/homelab/pve/reprepro/builders
ExecStart=/usr/local/bin/webhook -hooks webhook/hooks.json -verbose -port 9000
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Installation

cd pve/reprepro/builders
task install-service

๐Ÿงช Triggering a Build

You can trigger a build for a specific app (e.g., restic) by sending a JSON POST request.

Local Trigger

curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"app": "restic"}' \
  http://localhost:9000/hooks/build-app

๐Ÿง  Smart Caching

The builder uses a smart caching strategy to balance build speed and storage usage.

  • Incremental Builds: Existing source directories are updated (git fetch) instead of being deleted.
  • Cache Age: Caches older than BUILDER_CACHE_MAX_AGE_DAYS (set in .env) are automatically refreshed.
  • Compiler Caches: Rust (target/) and Go (GOCACHE) caches persist between builds.

โฐ Cronjob

A cronjob can be setup to run every night to check the released versions.

2 A.M. nightly

(crontab -l 2>/dev/null; echo "0 2 * * * /root/git/nicholaswilde/homelab/pve/reprepro/update-reprepro.sh") | crontab -
crontab -e
0 2 * * * /root/git/nicholaswilde/homelab/pve/reprepro/update-reprepro.sh

Copy from one dist to another

Find the full path of the file
find /srv/reprepro/debian/pool -name "localsend-cli*1.2.2*.deb"
Include that file into the new repository
reprepro -b /srv/reprepro/raspi/ includedeb trixie /srv/reprepro/debian/pool/main/l/localsend-cli/localsend-cli_1.2.2_armhf.deb

Remove app

reprepro -b /srv/reprepro/raspi remove bookworm sops
Remove Only a Specific Architecture
reprepro -b /srv/reprepro/raspi -A armhf remove bookworm sops
List Only a Specific Version
reprepro -b /srv/reprepro/raspi listfilter bookworm "Package (== sops), Version (== 1.0.2)"
Remove Only a Specific Version
reprepro -b /srv/reprepro/raspi removefilter bookworm "Package (== sops), Version (== 1.0.2)"
Cleanup
reprepro -b /srv/reprepro/raspi deleteunreferenced

Traefik

homelab/pve/traefik/conf.d/reprepro.yaml
---
http:
 #region routers 
  routers:
    reprepro:
      entryPoints:
        - "websecure"
      rule: "Host(`deb.l.nicholaswilde.io`)"
      middlewares:
        - default-headers@file
        - https-redirectscheme@file
      tls: {}
      service: reprepro
#endregion
#region services
  services:
    reprepro:
      loadBalancer:
        servers:
          - url: "http://192.168.1.58"
        passHostHeader: true
# #endregion

Task List

task: Available tasks for this project:
* bootstrap:             Bootstrap the reprepro environment
* clear:                 Remove all packages from all distributions
* decrypt:               Decrypt sensitive configuration files using SOPS.
* deps:                  Install dependencies
* deps-lnav:             Install lnav dependencies
* dirs:                  Create reprepro directories
* download:              Download SOPS and Task .deb files
* encrypt:               Encrypt sensitive configuration files using SOPS.
* export:                Export the task list
* init:                  Initialize the application's environment and configuration files.
* list:                  List all packages in all distributions
* nuke:                  Nuke the packages
* symlinks:              Create reprepro symlinks
* update-reprepro:       Downloads application tar.gz and .deb files, packages them as needed, and adds them to a reprepro repository.
* upload-debs:           Upload the deb packages to a remote server
* verify-secrets:        Checks if the decrypted secret matches the local file
* symlinks:test:         Create reprepro symlinks
* wh:install:            Install and start the systemd webhook service
* wh:logs:               View the webhook service logs
* wh:status:             Check the status of the systemd webhook service
* wh:test:               Test the webhook listener locally

๐Ÿ”— References