📦 Part 1: Setting up a Custom APT Repository Server on Debian 13 (Trixie)
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?
When distributing complex software like FreeSWITCH 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:
- Dependency Management: APT automatically resolves and installs system prerequisites.
- Security: Cryptographically signed packages ensure that clients only install untampered code.
- Consistency: Ensure all production, staging, and development servers run the exact same versions.
- Convenience: Updates are as simple as running
apt-get update && apt-get upgrade.
🛠️ 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.
1.1 Install System Utilities
First, update the package list and install Nginx alongside the tools required to generate repository metadata and manage cryptographic keys:
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)
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.
2.1 Create the Nginx Virtual Host
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:
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
Run Certbot to acquire and install an SSL certificate, automatically redirecting all HTTP traffic to HTTPS:
certbot --nginx -d repo.softswitchone.com
Configuring SSL Auto-Renewal
Let's Encrypt certificates are valid for 90 days. Set up a daily cron job to run the certbot renewal checks automatically:
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)
Modern APT clients refuse to install packages from repositories that do not sign their index metadata with a trusted GPG key.
3.1 Generate the RSA Signing Key
Run the key generator on your repository server:
Select the following configuration options when prompted:
- Key type: Select
1 (RSA and RSA).
- Keysize: Enter
4096 bits.
- Validity: Enter
0 (key does not expire) and confirm with y.
- Real name: Enter
Ring2All Repo.
- Email address: Enter
[EMAIL_ADDRESS].
- Passphrase: Leave empty (press Enter twice) for automated, non-interactive index signing.
3.2 Find and Copy the Key ID
List the keys in your keyring to retrieve the long key fingerprint:
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.
3.3 Export the Public Key for Clients
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:
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
To keep the repository clean and maintainable, we partition packages into five functional Components:
| Component | Pool Path | Content Description |
|---|
| base | pool/b/base/ | FreeSWITCH 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
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
4.2 Script 2: /usr/sbin/CleanPoolRepo
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
4.3 Script 3: /usr/sbin/BuildRepoRing2All
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
This script acts as the entry-point gatekeeper, sorting incoming compiled FreeSWITCH .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"
SRC_DIR="/usr/src/freeswitch/deb"
echo "=============================================="
echo " Copying packages to Ring2All Repository "
echo "=============================================="
mkdir -p $POOL_DIR/{a/audios,b/base,c/core,d/devel,e/extras}
# 1. BASE: FreeSWITCH Core + Custom Dependencies + Core Modules
echo ""
echo "=== BASE: FreeSWITCH Core ==="
cp -v $SRC_DIR/freeswitch_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-all_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-systemd*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-timezones*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/libfreeswitch1_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-meta-*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-conf-*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-lang*.deb $POOL_DIR/b/base/ 2>/dev/null
# Custom built dependencies
cp -v $SRC_DIR/libspandsp3_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/libks2_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-sofia-sip-ua0_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-sofia-sip-ua-glib3_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-sofia-sip-bin_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/libbroadvoice1_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/signalwire-client-c2_*.deb $POOL_DIR/b/base/ 2>/dev/null
# Essential SIP & Application Modules
cp -v $SRC_DIR/freeswitch-mod-sofia_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-verto_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-rtc_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-loopback_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-lua_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-commands_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-dptools_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-dialplan-xml_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-event-socket_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-console_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-logfile_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-syslog_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-pgsql_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-cdr-pg-csv_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-odbc-cdr_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-db_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-hash_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-opus_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-spandsp_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-g729_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-codec2_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-amr_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-amrwb_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-voicemail_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-voicemail-ivr_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-conference_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-fifo_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-callcenter_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-httapi_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-sndfile_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-tone-stream_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-local-stream_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-native-file_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-shout_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-sms_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-valet-parking_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-spy_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-directory_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-lcr_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-nibblebill_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-blacklist_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-cidlookup_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-curl_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-xml-curl_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-http-cache_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-fail2ban_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-expr_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-translate_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-distributor_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-easyroute_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-avmd_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-vmd_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-en_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-es_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-es-ar_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-cdr-csv_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-json-cdr_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-xml-cdr_*.deb $POOL_DIR/b/base/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-format-cdr_*.deb $POOL_DIR/b/base/ 2>/dev/null
# 2. EXTRAS: Auxiliary Dialplans, Databases, Languages, and Codecs
echo ""
echo "=== EXTRAS: Optional Modules ==="
cp -v $SRC_DIR/freeswitch-mod-perl_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-python3_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-java_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-erlang-event_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-dialplan-asterisk_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-dialplan-directory_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-mariadb_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-cdr-sqlite_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-cdr-mongodb_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-hiredis_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-memcache_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-redis_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-vlc_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-av_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-cv_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-imagick_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-png_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-video-filter_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-yuv_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-fsv_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-rtmp_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-skinny_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-alsa_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-de_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-fr_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-pt_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-ru_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-it_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-nl_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-he_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-hr_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-hu_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-ja_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-pl_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-sv_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-th_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-zh_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-say-fa_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-bv_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-g723-1_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-opusfile_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-b64_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-amqp_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-bert_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-enum_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-esf_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-esl_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-event-multicast_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-event-test_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-fsk_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-graylog2_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-pocketsphinx_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-posix-timer_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-prefix_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-random_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-shell-stream_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-signalwire_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-snapshot_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-snmp_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-test_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-timerfd_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-tts-commandline_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-xml-rpc_*.deb $POOL_DIR/e/extras/ 2>/dev/null
cp -v $SRC_DIR/freeswitch-mod-xml-scgi_*.deb $POOL_DIR/e/extras/ 2>/dev/null
# 3. DEVEL: Header files, Debug symbols, and ESL Bindings
echo ""
echo "=== DEVEL: Development Packages ==="
cp -v $SRC_DIR/*-dev_*.deb $POOL_DIR/d/devel/ 2>/dev/null
cp -v $SRC_DIR/*-dbg_*.deb $POOL_DIR/d/devel/ 2>/dev/null
cp -v $SRC_DIR/*-dbgsym_*.deb $POOL_DIR/d/devel/ 2>/dev/null
cp -v $SRC_DIR/*-doc_*.deb $POOL_DIR/d/devel/ 2>/dev/null
cp -v $SRC_DIR/python-esl*.deb $POOL_DIR/d/devel/ 2>/dev/null
cp -v $SRC_DIR/libesl-perl*.deb $POOL_DIR/d/devel/ 2>/dev/null
# 4. CORE & AUDIOS: Populated by separate component builds
echo ""
echo "=== CORE: Softswitch Packages ==="
echo "(Softswitch-core packages will be uploaded here)"
echo ""
echo "=== AUDIOS: Voice Guides ==="
echo "(Voiceguide packages will be uploaded here)"
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 "✅ Copy complete! Run: BuildRepoRing2All"
EOF
chmod +x /usr/sbin/CopyPackagesToRepo
🚀 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.
🔍 Step 6: Client Verification
To register a fresh Debian 13 target client server and install packages from our repository, run:
# 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
⏭️ What's Next?
Now that the distribution infrastructure is fully operational, we need packages to publish. In Part 2: Compiling and Packaging FreeSWITCH on Debian 13, we will compile the FreeSWITCH communication engine from source, package it as .deb binaries, and publish them to our new repository.