Skip to content

📦 Part 1: Setting up a Custom APT Repository Server on Debian 13 (Trixie)

16 min readUpdated: Sep 26, 2026
View as Markdown

Welcome to the first installment of our “Debian 13 Packaging & Distribution” series. In this post, we will walk through the process of building your own secure, public-facing Debian APT repository server from scratch. You will learn how to configure Nginx to serve package indices, generate and use GPG keys to sign your metadata, write utility scripts to manage package sorting and deduplication, and distribute a bootstrap script to make client installation as simple as a single command.


🏗️ Why Build a Custom APT Repository?

Section titled “🏗️ Why Build a Custom APT Repository?”

When distributing complex software like Telephony Server or Kamailio across multiple production servers, relying on manual file transfers or local compilation on every machine is error-prone and inefficient.

A custom APT repository provides:

  1. Dependency Management: APT automatically resolves and installs system prerequisites.
  2. Security: Cryptographically signed packages ensure that clients only install untampered code.
  3. Consistency: Ensure all production, staging, and development servers run the exact same versions.
  4. Convenience: Updates are as simple as running apt-get update && apt-get upgrade.

🛠️ Step 1: Server Preparation & Web Server Installation

Section titled “🛠️ Step 1: Server Preparation & Web Server Installation”

For this setup, we will use a server running Debian 13 (Trixie). Our repository will be hosted at repo.softswitchone.com.

First, update the package list and install Nginx alongside the tools required to generate repository metadata and manage cryptographic keys:

Terminal window
apt-get update
apt-get install -y nginx apt-utils devscripts gnupg2 dpkg-dev gzip python3 certbot python3-certbot-nginx
  • nginx: Serves the repository files over HTTP/HTTPS.
  • apt-utils & dpkg-dev: Contains dpkg-scanpackages to index package directories.
  • gnupg2: Cryptographically signs repository indexes.

🌐 Step 2: Web Server Configuration (Nginx & HTTPS)

Section titled “🌐 Step 2: Web Server Configuration (Nginx & HTTPS)”

APT repositories are served over simple HTTP/HTTPS directory structures. We will configure Nginx to serve our files and enable directory listing so APT can crawl the pool.

Create a configuration file at /etc/nginx/sites-available/repo.softswitchone.com:

server {
listen 80;
server_name repo.softswitchone.com;
root /var/www/pbx-repo/ring2all/trixie;
access_log /var/log/nginx/repo_access.log;
error_log /var/log/nginx/repo_error.log;
# Allow directory listing so clients can inspect package folders
autoindex on;
# Serve the main repository files
location /apt/ {
alias /var/www/pbx-repo/ring2all/trixie/apt/;
autoindex on;
}
# Public location for client GPG keys
location /apt/gpgkey/ {
alias /var/www/pbx-repo/ring2all/trixie/apt/gpgkey/;
}
# Deny access to hidden system files (.git, .htaccess, etc.)
location ~ /\. {
deny all;
}
}

Enable the configuration and restart Nginx:

Terminal window
ln -sf /etc/nginx/sites-available/repo.softswitchone.com /etc/nginx/sites-enabled/
nginx -t
systemctl restart nginx

2.2 Secure the Server with Let’s Encrypt SSL

Section titled “2.2 Secure the Server with Let’s Encrypt SSL”

Run Certbot to acquire and install an SSL certificate, automatically redirecting all HTTP traffic to HTTPS:

Terminal window
certbot --nginx -d repo.softswitchone.com

Let’s Encrypt certificates are valid for 90 days. Set up a daily cron job to run the certbot renewal checks automatically:

Terminal window
echo "0 3 * * * root certbot renew --quiet --post-hook 'systemctl reload nginx'" > /etc/cron.d/certbot-renew
chmod 644 /etc/cron.d/certbot-renew

🔑 Step 3: Cryptographic Signing (GPG Key Setup)

Section titled “🔑 Step 3: Cryptographic Signing (GPG Key Setup)”

Modern APT clients refuse to install packages from repositories that do not sign their index metadata with a trusted GPG key.

Run the key generator on your repository server:

Terminal window
gpg --full-generate-key

Select the following configuration options when prompted:

  1. Key type: Select 1 (RSA and RSA).
  2. Keysize: Enter 4096 bits.
  3. Validity: Enter 0 (key does not expire) and confirm with y.
  4. Real name: Enter Ring2All Repo.
  5. Email address: Enter [EMAIL_ADDRESS].
  6. Passphrase: Leave empty (press Enter twice) for automated, non-interactive index signing.

List the keys in your keyring to retrieve the long key fingerprint:

Terminal window
gpg --list-keys --keyid-format long

Example Output:

pub rsa4096/D899B67676A5D2D408C5E30EA4B1437798E981B7 2026-01-26 [SC]
D899B67676A5D2D408C5E30EA4B1437798E981B7
uid [ultimate] Ring2All Repo <[EMAIL_ADDRESS]>

Identify the long key ID: D899B67676A5D2D408C5E30EA4B1437798E981B7.

Clients will need your public GPG key to verify the repository signatures. Export it in ASCII-armored format and save it inside your web server root:

Terminal window
mkdir -p /var/www/pbx-repo/ring2all/trixie/apt/gpgkey
gpg --armor --export D899B67676A5D2D408C5E30EA4B1437798E981B7 > /var/www/pbx-repo/ring2all/trixie/apt/gpgkey/ring2all.gpg

📁 Step 4: Repository Structure & Automation Scripts

Section titled “📁 Step 4: Repository Structure & Automation Scripts”

To keep the repository clean and maintainable, we partition packages into five functional Components:

Component Pool Path Content Description
base pool/b/base/ Telephony core, dependencies, and essential modules
core pool/c/core/ Ring2All Softswitch packages (admin panel, APIs, etc.)
extras pool/e/extras/ Auxiliary modules (Perl, Python, MariaDB backends, VLC)
devel pool/d/devel/ Development headers (-dev) and debug symbols (-dbg)
audios pool/a/audios/ Voice guides (Emma, Paloma) and music files

Let’s configure the scripts that automate package sorting, deduplication, metadata indexing, and signing.

4.1 Script 1: /usr/sbin/ReleaseBuilderRing2All

Section titled “4.1 Script 1: /usr/sbin/ReleaseBuilderRing2All”

This script parses the index metadata directories and outputs standard Debian release header hashes (MD5, SHA1, SHA256) of all underlying files.

Create /usr/sbin/ReleaseBuilderRing2All:

cat > /usr/sbin/ReleaseBuilderRing2All <<'EOF'
#!/bin/bash
set -e
while getopts o:l:s:d:c:a: flag; do
case "${flag}" in
o) ORIGIN=${OPTARG};;
l) LABEL=${OPTARG};;
s) SUITE=${OPTARG};;
d) DESC=${OPTARG};;
c) COMP=${OPTARG};;
a) ARCH=${OPTARG};;
esac
done
ORIGIN=${ORIGIN:-"Ring2All Repo"}
LABEL=${LABEL:-Base}
SUITE=${SUITE:-stable}
DESC=${DESC:-"Ring2All packages"}
COMP=${COMP:-main}
ARCH=${ARCH:-"amd64 arm64 armhf all"}
do_hash() {
HASH_NAME=$1
HASH_CMD=$2
echo "${HASH_NAME}:"
for f in $(find -type f); do
f=$(echo $f | cut -c3-)
[ "$f" = "Release" ] && continue
echo " $(${HASH_CMD} ${f} | cut -d' ' -f1) $(wc -c < $f) $f"
done
}
cat <<EOT
Origin: ${ORIGIN}
Label: ${LABEL}
Suite: ${SUITE}
Codename: ${SUITE}
Version: 1.0
Architectures: ${ARCH}
Components: ${COMP}
Description: ${DESC}
Date: $(date -Ru)
EOT
do_hash "MD5Sum" "md5sum"
do_hash "SHA1" "sha1sum"
do_hash "SHA256" "sha256sum"
EOF
chmod +x /usr/sbin/ReleaseBuilderRing2All

If we upload packages repeatedly (e.g., during active CI/CD iterations), older versions will accumulate and consume disk space. This Python script compares package version numbers using Debian’s native dpkg --compare-versions and safely purges old duplicates while keeping only the latest version.

Create /usr/sbin/CleanPoolRepo:

cat > /usr/sbin/CleanPoolRepo <<'EOF'
#!/usr/bin/env python3
import os
import subprocess
import sys
def get_pkg_info(deb_path):
try:
res = subprocess.run(
["dpkg-deb", "-f", deb_path, "Package", "Version", "Architecture"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True
)
info = {}
for line in res.stdout.strip().split('\n'):
if ':' in line:
k, v = line.split(':', 1)
info[k.strip().lower()] = v.strip()
return info.get("package"), info.get("version"), info.get("architecture")
except Exception as e:
print(f"Error reading {deb_path}: {e}", file=sys.stderr)
return None, None, None
def compare_versions(v1, v2):
res = subprocess.run(["dpkg", "--compare-versions", v1, "lt", v2])
return res.returncode == 0
def clean_pool(pool_dir):
if not os.path.isdir(pool_dir):
print(f"Directory {pool_dir} does not exist.")
return
deb_files = []
for root, _, files in os.walk(pool_dir):
for f in files:
if f.endswith(".deb"):
deb_files.append(os.path.join(root, f))
groups = {}
for filepath in deb_files:
pkg, ver, arch = get_pkg_info(filepath)
if not pkg or not ver or not arch:
continue
key = (pkg, arch)
if key not in groups:
groups[key] = []
groups[key].append({"path": filepath, "version": ver})
for key, items in groups.items():
if len(items) <= 1:
continue
for i in range(len(items)):
max_idx = i
for j in range(i + 1, len(items)):
if compare_versions(items[max_idx]["version"], items[j]["version"]):
max_idx = j
items[i], items[max_idx] = items[max_idx], items[i]
latest = items[0]
for item in items[1:]:
print(f"Removing older version: {item['path']} ({item['version']}) in favor of {latest['version']}")
try:
os.remove(item["path"])
except Exception as e:
print(f"Error removing {item['path']}: {e}", file=sys.stderr)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: CleanPoolRepo <pool_dir>")
sys.exit(1)
clean_pool(sys.argv[1])
EOF
chmod +x /usr/sbin/CleanPoolRepo

This script links all pools, triggers the cleanup process, scans packages using dpkg-scanpackages, generates compressed metadata indexes (Packages.gz), builds the global Release files, and signs them cryptographically with GPG.

Create /usr/sbin/BuildRepoRing2All:

cat > /usr/sbin/BuildRepoRing2All <<'EOF'
#!/bin/bash
set -e
PROJECT_DIR=/var/www/pbx-repo/ring2all/trixie
# Detect GPG key matching the administrator's email
GPG_KEY=$(gpg --list-secret-keys --keyid-format long "[EMAIL_ADDRESS]" 2>/dev/null | grep -A 1 "^sec" | tail -n 1 | awk '{print $1}')
if [ -z "$GPG_KEY" ]; then
GPG_KEY=$(gpg --list-secret-keys --keyid-format long 2>/dev/null | grep -A 1 "^sec" | tail -n 1 | awk '{print $1}')
fi
GPG_KEY=${GPG_KEY:-"D899B67676A5D2D408C5E30EA4B1437798E981B7"}
echo "Creating directory structures..."
mkdir -p ${PROJECT_DIR}/apt/{gpgkey,base,core,extras,devel,audios}
mkdir -p ${PROJECT_DIR}/apt/pool/{a/audios,b/base,c/core,e/extras,d/devel}
for section in audios base core extras devel; do
mkdir -p ${PROJECT_DIR}/apt/${section}/dists/stable/main/binary-{amd64,all,arm64,armhf}
mkdir -p ${PROJECT_DIR}/apt/${section}/pool
done
# Map virtual symbolic links to resolve the centralized pool
ln -sf ${PROJECT_DIR}/apt/pool ${PROJECT_DIR}/apt/audios/pool 2>/dev/null || true
ln -sf ${PROJECT_DIR}/apt/pool ${PROJECT_DIR}/apt/base/pool 2>/dev/null || true
ln -sf ${PROJECT_DIR}/apt/pool ${PROJECT_DIR}/apt/core/pool 2>/dev/null || true
ln -sf ${PROJECT_DIR}/apt/pool ${PROJECT_DIR}/apt/extras/pool 2>/dev/null || true
ln -sf ${PROJECT_DIR}/apt/pool ${PROJECT_DIR}/apt/devel/pool 2>/dev/null || true
# Copy signing key and client bootstrap script to public web paths
[ -f ~/ring2all.gpg ] && cp ~/ring2all.gpg ${PROJECT_DIR}/apt/gpgkey/ring2all.gpg
[ -f ~/ring2all_setup_repo ] && cp ~/ring2all_setup_repo ${PROJECT_DIR}/apt/setup_repo
# Purge obsolete versions
if [ -x /usr/sbin/CleanPoolRepo ]; then
echo "Purging old duplicate packages from pool..."
/usr/sbin/CleanPoolRepo ${PROJECT_DIR}/apt/pool
fi
# Rebuild package indexes
echo "Generating Packages files..."
cd ${PROJECT_DIR}/apt
for section in audios base core extras devel; do
pool_letter="${section:0:1}"
[ "$section" = "audios" ] && pool_letter="a"
[ "$section" = "extras" ] && pool_letter="e"
for arch in amd64 all arm64 armhf; do
dpkg-scanpackages --arch $arch pool/${pool_letter}/${section}/ /dev/null > ${section}/dists/stable/main/binary-${arch}/Packages 2>/dev/null || true
gzip -9c ${section}/dists/stable/main/binary-${arch}/Packages > ${section}/dists/stable/main/binary-${arch}/Packages.gz 2>/dev/null || true
done
done
# Create and cryptographically sign release metadata
echo "Signing release index..."
for section in audios base core extras devel; do
cd ${PROJECT_DIR}/apt/${section}/dists/stable/
/usr/sbin/ReleaseBuilderRing2All -l ${section^} -d "Ring2All ${section^} packages" > Release
rm -f Release.gpg InRelease
if gpg --list-keys "$GPG_KEY" >/dev/null 2>&1; then
gpg -u "$GPG_KEY" -abs -o Release.gpg Release
cat Release | gpg -u "$GPG_KEY" -abs --clearsign > InRelease
else
echo "⚠️ WARNING: GPG key $GPG_KEY not found. Skipping metadata signing."
fi
done
echo "✅ Repository rebuild complete!"
EOF
chmod +x /usr/sbin/BuildRepoRing2All

4.4 Script 4: /usr/sbin/CopyPackagesToRepo

Section titled “4.4 Script 4: /usr/sbin/CopyPackagesToRepo”

This script acts as the entry-point gatekeeper, sorting incoming compiled Telephony Server .deb packages (e.g. core binaries, configuration packs, dynamic modules, and custom telephony dependencies) into their respective repository component directories.

Create /usr/sbin/CopyPackagesToRepo:

cat > /usr/sbin/CopyPackagesToRepo << 'EOF'
#!/bin/bash
# CopyPackagesToRepo - Sort uploaded packages into repository categories
POOL_DIR="/var/www/pbx-repo/ring2all/trixie/apt/pool"
# Detect source directory dynamically
SRC_DIR="/usr/src"
if [ -d "/usr/src/freeswitch/deb" ] && ls /usr/src/freeswitch/deb/*.deb &>/dev/null; then
SRC_DIR="/usr/src/freeswitch/deb"
fi
echo "=============================================="
echo " Moving packages to Ring2All Repository "
echo "=============================================="
echo "Source directory: $SRC_DIR"
echo "Target pool directory: $POOL_DIR"
# Create directories if needed
mkdir -p $POOL_DIR/{a/audios,b/base,c/core,d/devel,e/extras}
# ============================================================
# BASE: Telephony Core + Dependencies + Essential Modules
# ============================================================
echo ""
echo "=== BASE: Telephony Core ==="
# Core Telephony Server
mv -v $SRC_DIR/freeswitch_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-all_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-systemd*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-timezones*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/libfreeswitch1_*.deb $POOL_DIR/b/base/ 2>/dev/null
# Meta packages
mv -v $SRC_DIR/freeswitch-meta-*.deb $POOL_DIR/b/base/ 2>/dev/null
# Configuration packages
mv -v $SRC_DIR/freeswitch-conf-*.deb $POOL_DIR/b/base/ 2>/dev/null
# Language packs
mv -v $SRC_DIR/freeswitch-lang*.deb $POOL_DIR/b/base/ 2>/dev/null
# Dependencies (spandsp, libks2, sofia-sip, signalwire-c, broadvoice)
mv -v $SRC_DIR/libspandsp*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/libks2_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/libbroadvoice1_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/signalwire-client-c2_*.deb $POOL_DIR/b/base/ 2>/dev/null
# Legacy Sofia-SIP packages
mv -v $SRC_DIR/libsofia-sip-ua0_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/libsofia-sip-ua-glib3_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/sofia-sip-bin_*.deb $POOL_DIR/b/base/ 2>/dev/null
# New renamed Sofia-SIP packages
mv -v $SRC_DIR/freeswitch-sofia-sip-ua0_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-sofia-sip-ua-glib3_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-sofia-sip-bin_*.deb $POOL_DIR/b/base/ 2>/dev/null
# Kamailio SBC packages
mv -v $SRC_DIR/sbc-kamailio_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/sbc-kamailio-*-modules_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/sbc-kamailio-nth_*.deb $POOL_DIR/b/base/ 2>/dev/null
# Essential endpoint modules
mv -v $SRC_DIR/freeswitch-mod-sofia_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-verto_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-rtc_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-loopback_*.deb $POOL_DIR/b/base/ 2>/dev/null
# Essential application modules
mv -v $SRC_DIR/freeswitch-mod-lua_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-commands_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-dptools_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-dialplan-xml_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-event-socket_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-console_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-logfile_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-syslog_*.deb $POOL_DIR/b/base/ 2>/dev/null
# Database modules
mv -v $SRC_DIR/freeswitch-mod-pgsql_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-cdr-pg-csv_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-odbc-cdr_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-db_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-hash_*.deb $POOL_DIR/b/base/ 2>/dev/null
# Essential codecs
mv -v $SRC_DIR/freeswitch-mod-opus_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-spandsp_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-g729_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-codec2_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-amr_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-amrwb_*.deb $POOL_DIR/b/base/ 2>/dev/null
# Essential applications
mv -v $SRC_DIR/freeswitch-mod-voicemail_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-voicemail-ivr_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-conference_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-fifo_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-callcenter_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-httapi_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-sndfile_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-tone-stream_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-local-stream_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-native-file_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-shout_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-sms_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-valet-parking_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-spy_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-directory_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-lcr_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-nibblebill_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-blacklist_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-cidlookup_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-curl_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-xml-curl_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-http-cache_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-fail2ban_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-expr_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-translate_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-distributor_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-easyroute_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-avmd_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-vmd_*.deb $POOL_DIR/b/base/ 2>/dev/null
# Say modules (essential for IVR)
mv -v $SRC_DIR/freeswitch-mod-say-en_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-es_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-es-ar_*.deb $POOL_DIR/b/base/ 2>/dev/null
# CDR modules
mv -v $SRC_DIR/freeswitch-mod-cdr-csv_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-json-cdr_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-xml-cdr_*.deb $POOL_DIR/b/base/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-format-cdr_*.deb $POOL_DIR/b/base/ 2>/dev/null
# ============================================================
# EXTRAS: Optional modules
# ============================================================
echo ""
echo "=== EXTRAS: Optional Modules ==="
# Alternative language bindings
mv -v $SRC_DIR/freeswitch-mod-perl_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-python3_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-java_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-erlang-event_*.deb $POOL_DIR/e/extras/ 2>/dev/null
# Alternative dialplans
mv -v $SRC_DIR/freeswitch-mod-dialplan-asterisk_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-dialplan-directory_*.deb $POOL_DIR/e/extras/ 2>/dev/null
# Alternative databases
mv -v $SRC_DIR/freeswitch-mod-mariadb_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-cdr-sqlite_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-cdr-mongodb_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-hiredis_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-memcache_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-redis_*.deb $POOL_DIR/e/extras/ 2>/dev/null
# Media modules
mv -v $SRC_DIR/freeswitch-mod-vlc_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-av_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-cv_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-imagick_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-png_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-video-filter_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-yuv_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-fsv_*.deb $POOL_DIR/e/extras/ 2>/dev/null
# Alternative endpoints
mv -v $SRC_DIR/freeswitch-mod-rtmp_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-skinny_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-alsa_*.deb $POOL_DIR/e/extras/ 2>/dev/null
# Additional say modules
mv -v $SRC_DIR/freeswitch-mod-say-de_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-fr_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-pt_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-ru_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-it_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-nl_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-he_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-hr_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-hu_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-ja_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-pl_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-sv_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-th_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-zh_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-say-fa_*.deb $POOL_DIR/e/extras/ 2>/dev/null
# Additional codecs
mv -v $SRC_DIR/freeswitch-mod-bv_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-g723-1_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-opusfile_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-b64_*.deb $POOL_DIR/e/extras/ 2>/dev/null
# Miscellaneous
mv -v $SRC_DIR/freeswitch-mod-amqp_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-bert_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-enum_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-esf_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-esl_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-event-multicast_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-event-test_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-fsk_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-graylog2_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-ldap_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-pocketsphinx_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-posix-timer_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-prefix_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-random_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-shell-stream_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-signalwire_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-snapshot_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-snmp_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-test_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-timerfd_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-tts-commandline_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-xml-rpc_*.deb $POOL_DIR/e/extras/ 2>/dev/null
mv -v $SRC_DIR/freeswitch-mod-xml-scgi_*.deb $POOL_DIR/e/extras/ 2>/dev/null
# ============================================================
# DEVEL: Development packages and debug symbols
# ============================================================
echo ""
echo "=== DEVEL: Development Packages ==="
mv -v $SRC_DIR/*-dev_*.deb $POOL_DIR/d/devel/ 2>/dev/null
mv -v $SRC_DIR/*-dbg_*.deb $POOL_DIR/d/devel/ 2>/dev/null
mv -v $SRC_DIR/*-dbgsym_*.deb $POOL_DIR/d/devel/ 2>/dev/null
mv -v $SRC_DIR/*-doc_*.deb $POOL_DIR/d/devel/ 2>/dev/null
mv -v $SRC_DIR/python-esl*.deb $POOL_DIR/d/devel/ 2>/dev/null
mv -v $SRC_DIR/libesl-perl*.deb $POOL_DIR/d/devel/ 2>/dev/null
# ============================================================
# CORE: Ring2All PBX, Ring2All SBC & Ring2All BSS Packages
# ============================================================
echo ""
echo "=== CORE: Ring2All PBX, Ring2All SBC & Ring2All BSS Packages ==="
# Ring2All PBX Packages
mv -v $SRC_DIR/softswitch-admin_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-db_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-api_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-monitoring-api_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-billing-api_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-portal_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-switchboard_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-telephony_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-all_*.deb $POOL_DIR/c/core/ 2>/dev/null
# Ring2All SBC Packages (Modular Suite & Monolithic)
mv -v $SRC_DIR/softswitch-sbc_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-sbc-db_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-sbc-api_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-sbc-admin_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-sbc-telephony_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-sbc-all_*.deb $POOL_DIR/c/core/ 2>/dev/null
# Ring2All BSS Packages (Billing, OCS & Customer Store)
mv -v $SRC_DIR/softswitch-bss-api_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-bss-web_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-bss-client_*.deb $POOL_DIR/c/core/ 2>/dev/null
mv -v $SRC_DIR/softswitch-bss-all_*.deb $POOL_DIR/c/core/ 2>/dev/null
# Legacy or generic audios in core (music)
mv -v $SRC_DIR/softswitch-music_*.deb $POOL_DIR/c/core/ 2>/dev/null
# ============================================================
# AUDIOS: Voice guides (populated by voiceguide builds)
# ============================================================
echo ""
echo "=== AUDIOS: Voice Guides ==="
mv -v $SRC_DIR/softswitch-voiceguide-emma_*.deb $POOL_DIR/a/audios/ 2>/dev/null
mv -v $SRC_DIR/softswitch-voiceguide-paloma_*.deb $POOL_DIR/a/audios/ 2>/dev/null
# ============================================================
# Count packages per component
# ============================================================
echo ""
echo "=============================================="
echo " Summary "
echo "=============================================="
echo "BASE: $(ls -1 $POOL_DIR/b/base/*.deb 2>/dev/null | wc -l) packages"
echo "CORE: $(ls -1 $POOL_DIR/c/core/*.deb 2>/dev/null | wc -l) packages"
echo "EXTRAS: $(ls -1 $POOL_DIR/e/extras/*.deb 2>/dev/null | wc -l) packages"
echo "DEVEL: $(ls -1 $POOL_DIR/d/devel/*.deb 2>/dev/null | wc -l) packages"
echo "AUDIOS: $(ls -1 $POOL_DIR/a/audios/*.deb 2>/dev/null | wc -l) packages"
echo ""
echo "✅ Done! Now run: BuildRepoRing2All"
EOF
chmod +x /usr/sbin/CopyPackagesToRepo

🚀 Step 5: Client Installation & Bootstrap Configuration

Section titled “🚀 Step 5: Client Installation & Bootstrap Configuration”

On the repository server, we create a script ring2all_setup_repo to automate client registration. This script will download the signing GPG key, save it securely under /etc/apt/keyrings, and configure the custom repository list.

Create ~/ring2all_setup_repo:

cat > ~/ring2all_setup_repo << 'EOF'
#!/bin/bash
set -e
echo "Installing Ring2All repository keyring and source list..."
# Install basic dependencies if missing
apt-get update && apt-get install -y wget curl gpg
# Create keyrings directory
mkdir -p /etc/apt/keyrings
# Securely download and import public signing key
# Securely download and import public signing key
curl -fsSL https://repo.softswitchone.com/apt/gpgkey/ring2all.gpg | gpg --dearmor -o /etc/apt/keyrings/ring2all.gpg
chmod 644 /etc/apt/keyrings/ring2all.gpg
# Register the components source file
cat > /etc/apt/sources.list.d/ring2all.list << 'SOURCES'
deb [signed-by=/etc/apt/keyrings/ring2all.gpg] https://repo.softswitchone.com/apt/base stable main
deb [signed-by=/etc/apt/keyrings/ring2all.gpg] https://repo.softswitchone.com/apt/devel stable main
deb [signed-by=/etc/apt/keyrings/ring2all.gpg] https://repo.softswitchone.com/apt/extras stable main
deb [signed-by=/etc/apt/keyrings/ring2all.gpg] https://repo.softswitchone.com/apt/audios stable main
deb [signed-by=/etc/apt/keyrings/ring2all.gpg] https://repo.softswitchone.com/apt/core stable main
SOURCES
echo "✅ Ring2All repository configured successfully!"
echo "Run 'apt-get update' to refresh the package cache."
EOF
chmod +x ~/ring2all_setup_repo

Whenever /usr/sbin/BuildRepoRing2All is executed, it automatically publishes this setup script to the Nginx web root.


To register a fresh Debian 13 target client server and install packages from our repository, run:

Terminal window
# Bootstrap the client
wget -qO- https://repo.softswitchone.com/apt/setup_repo | bash
# Refresh APT cache
apt-get update
# Install from the new repository
apt-get install -y freeswitch freeswitch-meta-all

Now that the distribution infrastructure is fully operational, we need packages to publish. In Part 2: Compiling and Packaging Telephony Server Engine on Debian 13, we will compile the Telephony Server communication engine from source, package it as .deb binaries, and publish them to our new repository.