Ctrl K
ring2all.com

📦 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:

  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

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:

Bash
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:

Nginx
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:

Bash
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:

Bash
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:

Bash
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:

Bash
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.

3.2 Find and Copy the Key ID

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

Bash
gpg --list-keys --keyid-format long

Example Output:*

Text
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:

Bash
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:

ComponentPool PathContent Description
basepool/b/base/FreeSWITCH core, dependencies, and essential modules
corepool/c/core/Ring2All Softswitch packages (admin panel, APIs, etc.)
extraspool/e/extras/Auxiliary modules (Perl, Python, MariaDB backends, VLC)
develpool/d/devel/Development headers (-dev) and debug symbols (-dbg)
audiospool/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:

Bash
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:

Bash
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:

Bash
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:

Bash
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:

Bash
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:

Bash
# 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.

AI Assistant

👋 Hello! I'm your Ring2All documentation assistant. I can help you find information about configuring and using the Ring2All PBX platform.

How can I help you today?