Ctrl K
ring2all.com

📞 Part 2: Compiling and Packaging FreeSWITCH on Debian 13 (Trixie)

Welcome to the second installment of our "Debian 13 Packaging & Distribution" series. In the previous post, we successfully built and secured our custom Debian APT repository server. In this guide, we will focus on compiling the core communications engine, FreeSWITCH (v1.11.1), along with its custom dependencies (SpanDSP, Sofia-SIP, libks2, signalwire-c, and libbroadvoice) from source. We will also address complex VM and clock-skew compilation traps, generate modular .deb packages, and publish them to our central repository.

🛠️ Phase 1: Setting Up the Build Environment

Debian 13 (Trixie) utilizes modern compiler toolchains. To prepare your build node (which can be a dedicated VM or container running Debian 13), install the core development utilities.

1.1 Install Packaging and Compiling Tools

Bash
apt-get update
apt-get install -y \
  sudo git build-essential devscripts dpkg-dev equivs fakeroot \
  autoconf automake libtool libtool-bin cmake pkg-config \
  wget curl unzip python-is-python3 python3-all-dev \
  erlang-dev libtpl-dev libgdbm-dev libdb-dev \
  python3-setuptools docbook-xsl xsltproc \
  libglib2.0-dev graphviz dh-make dh-python

1.2 Install FreeSWITCH Compilation Prerequisites

FreeSWITCH relies on system libraries for features like SSL, database connectivity, and codecs. Run the following command to install the required header files:

Bash
apt-get install -y \
  uuid-dev zlib1g-dev libjpeg-dev libsqlite3-dev libcurl4-openssl-dev \
  libpcre2-dev libspeex-dev libspeexdsp-dev libldns-dev libedit-dev \
  libtiff5-dev yasm libopus-dev libsndfile1-dev libavformat-dev \
  libswscale-dev liblua5.2-dev liblua5.2-0 libpq-dev unixodbc-dev \
  libxml2-dev libpq5 sngrep libswresample-dev bison doxygen \
  libjansson-dev libopencv-dev libhiredis-dev libmemcached-dev \
  libsphinxbase-dev libpocketsphinx-dev libopencore-amrnb-dev \
  libmariadb-dev libperl-dev libgdbm-compat-dev librabbitmq-dev \
  libsnmp-dev libmagickcore-dev libopusfile-dev libmp3lame-dev \
  libshout3-dev libvlc-dev default-jdk mono-mcs libasound2-dev \
  libcodec2-dev python-dev-is-python3

NOTE
PCRE Library Warning: Debian 13 completely removes libpcre3-dev (PCRE 8.x, which has reached End-of-Life). We use the modern replacement, libpcre2-dev, which FreeSWITCH supports natively.

📦 Phase 2: Building Telephony Dependencies

Several libraries required by FreeSWITCH are either outdated in Debian's official repository or require specific patches for stability. We compile them first, generate their .deb files, and install them so they are recognized during the main FreeSWITCH configuration phase.

2.1 spandsp (v3.0)

SpanDSP handles critical telephony tasks such as T.38 faxing, tone detection, and classic modems.

Bash
cd /usr/src
git clone https://github.com/freeswitch/spandsp.git
cd spandsp
./bootstrap.sh
./configure

fakeroot debian/rules clean
dpkg-buildpackage -us -uc -b

# Isolate packages
mkdir -p /usr/src/freeswitch/deb
mv /usr/src/libspandsp3*.deb /usr/src/freeswitch/deb/

# Install dependencies locally to build FreeSWITCH
cd /usr/src/freeswitch/deb
dpkg -i libspandsp3_*.deb libspandsp3-dev_*.deb
apt-get install -f -y

2.2 libks (v2.0)

This SignalWire communication library requires a custom Debian packaging structure.

Bash
cd /usr/src
git clone https://github.com/signalwire/libks.git
cd libks
mkdir -p debian

Create the required packaging files in the debian/ directory directly:

2.2.1 Control File (debian/control)

Defines the source and binary packages, their metadata, build-time dependencies, and runtime dependencies.

Bash
cat <<'EOF' > debian/control
Source: libks
Section: libs
Priority: optional
Maintainer: [YOUR_FULL_NAME] <[EMAIL_ADDRESS]>
Build-Depends: debhelper-compat (= 13), cmake, libpcre2-dev, uuid-dev
Standards-Version: 4.6.2
Homepage: https://github.com/signalwire/libks

Package: libks2
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: SignalWire libks library
 A library for communication protocols developed by SignalWire.

Package: libks2-dev
Architecture: any
Depends: libks2 (= ${binary:Version}), ${misc:Depends}
Description: Development files for libks
 Development headers and libraries for libks.
EOF

2.2.2 Rules File (debian/rules)

The makefile containing rules that dictate how the package is configured, built, and installed into temporary directories.

Bash
cat <<'EOF' > debian/rules
#!/usr/bin/make -f
%:
	dh $@

override_dh_auto_configure:
	cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release .

override_dh_auto_install:
	dh_auto_install --destdir=debian/tmp

override_dh_auto_clean:
	dh_clean

override_dh_installdocs:
	dh_installdocs
	dh_installdocs -plibks2-dev --link-doc=libks2
EOF

Make the script executable:

Bash
chmod +x debian/rules

2.2.3 Changelog File (debian/changelog)

Maintains the release history, package versioning, and change list.

Bash
cat <<'EOF' > debian/changelog
libks (2.0-7) unstable; urgency=medium

  * Initial packaging for Debian 13.

 -- [YOUR_FULL_NAME] <[EMAIL_ADDRESS]>  Mon, 08 Sep 2025 20:30:00 +0000
EOF

Documents the license and copyright information for the package.

Bash
cat <<'EOF' > debian/copyright
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Files: *
Copyright: 2025 SignalWire, Inc.
License: MPL-1.1
EOF

2.2.5 Library Install File (debian/libks2.install)

Specifies which built files (libraries) go into the runtime binary package (libks2).

Bash
cat <<'EOF' > debian/libks2.install
usr/lib/libks2.so*
EOF

2.2.6 Development Install File (debian/libks2-dev.install)

Specifies development assets (header files and pkg-config manifests) that go into the development package.

Bash
cat <<'EOF' > debian/libks2-dev.install
usr/include/libks2/*
usr/lib/pkgconfig/libks2.pc
EOF

2.2.7 Unpackaged Files List (debian/not-installed)

Tells the build system to ignore generated files that are intentionally not packaged (such as the default changelog and copyright files).

Bash
cat <<'EOF' > debian/not-installed
usr/share/doc/libks2/changelog.Debian.gz
usr/share/doc/libks2/copyright
EOF

Now, clean, build, and install the package:

Bash
# Clean potential CMake residuals from manual builds
rm -rf CMakeCache.txt CMakeFiles/ cmake_install.cmake Makefile obj-*
fakeroot debian/rules clean
DEB_BUILD_OPTIONS="nocheck" dpkg-buildpackage -us -uc -b

mv /usr/src/libks2*.deb /usr/src/freeswitch/deb/
cd /usr/src/freeswitch/deb
dpkg -i libks2_*.deb libks2-dev_*.deb
apt-get install -f -y

2.3 sofia-sip

Sofia-SIP is the underlying SIP user-agent library used by FreeSWITCH's endpoint module mod_sofia.

IMPORTANT
Preventing Collisions with Official Debian Repository Packages:

The official Debian 13 (Trixie) repositories distribute their own standard (unpatched) versions of Sofia-SIP (libsofia-sip-ua0 and libsofia-sip-ua-dev). To prevent a routine system update (apt upgrade) from overwriting our custom patched Sofia-SIP packages with the official Debian ones, we rename all Sofia-SIP binary packages to use the prefix freeswitch-sofia-sip instead of libsofia-sip. We also declare Provides, Conflicts, and Replaces directives pointing to the official Debian package names in debian/control so that FreeSWITCH's compilation and runtime dependencies are fully satisfied without conflicts.

Bash
cd /usr/src
git clone https://github.com/freeswitch/sofia-sip.git
cd sofia-sip
./bootstrap.sh
./configure

# Rename source and binary packages in debian/control and add Provides/Conflicts/Replaces
python3 -c "
import re
content = open('debian/control').read()
content = content.replace('libsofia-sip', 'freeswitch-sofia-sip')
content = re.sub(r'(?<!freeswitch-)sofia-sip', 'freeswitch-sofia-sip', content)

packages = {
    'freeswitch-sofia-sip-bin': 'sofia-sip-bin',
    'freeswitch-sofia-sip-ua0': 'libsofia-sip-ua0',
    'freeswitch-sofia-sip-ua-dev': 'libsofia-sip-ua-dev',
    'freeswitch-sofia-sip-ua-glib3': 'libsofia-sip-ua-glib3',
    'freeswitch-sofia-sip-ua-glib-dev': 'libsofia-sip-ua-glib-dev',
}
for pkg, orig in packages.items():
    pattern = rf'(Package:\s*{pkg}\s*\nArchitecture:\s*any\s*\n)'
    replacement = rf'\1Provides: {orig}\nConflicts: {orig}\nReplaces: {orig}\n'
    content = re.sub(pattern, replacement, content)

open('debian/control', 'w').write(content)
"

# Rename installation files in debian/ to match the new package names
python3 -c "
import os
for f in os.listdir('debian'):
    if 'sofia-sip' in f:
        if f.startswith('libsofia-sip'):
            new_f = 'freeswitch-sofia-sip' + f[len('libsofia-sip'):]
        elif f.startswith('sofia-sip'):
            new_f = 'freeswitch-sofia-sip' + f[len('sofia-sip'):]
        else:
            new_f = f
        os.rename(os.path.join('debian', f), os.path.join('debian', new_f))
"

# Update source package name in debian/changelog
sed -i 's/^sofia-sip/freeswitch-sofia-sip/g' debian/changelog

# Add a custom changelog entry with the +softswitch version suffix
dch -v "1.13.17-0+softswitch" "Custom compile with nua_reload_tls patch and freeswitch-sofia package rename"

fakeroot debian/rules clean
dpkg-buildpackage -us -uc -b

# Move packages to /usr/src/freeswitch/deb
mv /usr/src/freeswitch-sofia-sip*.deb /usr/src/freeswitch/deb/

cd /usr/src/freeswitch/deb
dpkg -i freeswitch-sofia-sip-ua0_*.deb freeswitch-sofia-sip-ua-dev_*.deb freeswitch-sofia-sip-bin_*.deb
apt-get install -f -y

IMPORTANT
Package Renaming, Version Suffixing & Multi-Architecture Pitfalls
  • Package Renaming (freeswitch-sofia-sip): Since the official Debian repositories contain standard unpatched versions of libsofia-sip-ua0, we prefix our packages with freeswitch-sofia and build with version suffixing (+softswitch). This prevents apt from ever overwriting our library with official Debian repository updates, while our Provides, Conflicts, and Replaces directives ensure seamless dependency compatibility for FreeSWITCH's compilation and runtime requirements.
* Arch-Specific Repository Syncing: When deploying to production servers running different CPU architectures (e.g. some running arm64 and others running amd64), make sure you compile Sofia-SIP and other custom dependencies on both CPU architectures and publish them to your APT repository. If an architecture is missing from the repository, target servers of that architecture will fall back to the unpatched upstream Debian libraries, breaking SIP signaling.

2.4 libbroadvoice

This codec library handles BroadVoice 16/32-bit audio profiles.

Bash
cd /usr/src
git clone https://github.com/freeswitch/libbroadvoice.git
cd libbroadvoice
./autogen.sh
./configure

fakeroot debian/rules clean
dpkg-buildpackage -us -uc -b

mv /usr/src/libbroadvoice*.deb /usr/src/freeswitch/deb/
cd /usr/src/freeswitch/deb
dpkg -i libbroadvoice1_*.deb libbroadvoice-dev_*.deb

2.5 signalwire-c

Create the SignalWire C API library.

Bash
cd /usr/src
git clone https://github.com/signalwire/signalwire-c.git
cd signalwire-c
mkdir -p debian

Create the required packaging files in the debian/ directory directly:

2.5.1 Control File (debian/control)

Defines the source and binary packages, their metadata, build-time dependencies, and runtime dependencies.

Bash
cat <<'EOF' > debian/control
Source: signalwire-c
Section: libs
Priority: optional
Maintainer: [YOUR_FULL_NAME] <[EMAIL_ADDRESS]>
Build-Depends: debhelper-compat (= 13), cmake, libpcre2-dev, uuid-dev, libssl-dev, libjansson-dev
Standards-Version: 4.6.2
Homepage: https://github.com/signalwire/signalwire-c

Package: signalwire-client-c2
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libks2
Description: SignalWire C client library (version 2)

Package: signalwire-client-c2-dev
Architecture: any
Depends: signalwire-client-c2 (= ${binary:Version}), ${misc:Depends}, libks2-dev
Description: Development files for SignalWire C client library (version 2)
EOF

2.5.2 Rules File (debian/rules)

The makefile containing rules that dictate how the package is configured, built, and installed into temporary directories.

Bash
cat <<'EOF' > debian/rules
#!/usr/bin/make -f
export DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)

%:
	dh $@

override_dh_auto_configure:
	cmake -DCMAKE_INSTALL_PREFIX=/usr \
	      -DCMAKE_BUILD_TYPE=Release \
	      -DBUILD_SHARED_LIBS=ON \
	      -DINSTALL_PKGCONFIG_DIR=/usr/lib/$(DEB_HOST_MULTIARCH)/pkgconfig .

override_dh_auto_install:
	dh_auto_install --destdir=debian/tmp
	mkdir -p debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/pkgconfig
	mv debian/tmp/usr/lib/pkgconfig/signalwire_client2.pc debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/pkgconfig/
	rmdir debian/tmp/usr/lib/pkgconfig

override_dh_auto_test:
	: # Skip tests

override_dh_auto_clean:
	dh_clean

override_dh_installdocs:
	dh_installdocs
	dh_installdocs -psignalwire-client-c2-dev --link-doc=signalwire-client-c2
EOF

Make the script executable:

Bash
chmod +x debian/rules

2.5.3 Changelog File (debian/changelog)

Maintains the release history, package versioning, and change list.

Bash
cat <<'EOF' > debian/changelog
signalwire-c (1.0-13) unstable; urgency=medium

  * Initial package packaging for Debian 13.

 -- [YOUR_FULL_NAME] <[EMAIL_ADDRESS]>  Mon, 08 Sep 2025 22:00:00 +0000
EOF

2.5.4 Install File (debian/signalwire-client-c2.install)

Specifies which built files (libraries) go into the runtime binary package (signalwire-client-c2).

Bash
cat <<'EOF' > debian/signalwire-client-c2.install
usr/lib/libsignalwire_client2.so*
EOF

2.5.5 Development Install File (debian/signalwire-client-c2-dev.install)

Specifies development assets (header files and pkg-config manifests) that go into the development package.

Bash
cat <<'EOF' > debian/signalwire-client-c2-dev.install
usr/include/signalwire-client-c2/*
usr/lib/*/pkgconfig/signalwire_client2.pc
EOF

2.5.6 Unpackaged Files List (debian/not-installed)

Tells the build system to ignore generated files that are intentionally not packaged (such as the default changelog and copyright files).

Bash
cat <<'EOF' > debian/not-installed
usr/share/doc/signalwire-client-c2/changelog.Debian.gz
usr/share/doc/signalwire-client-c2/copyright
EOF

Build and install:

Bash
fakeroot debian/rules clean
dpkg-buildpackage -us -uc -b

mv /usr/src/signalwire-client-c2*.deb /usr/src/freeswitch/deb/
cd /usr/src/freeswitch/deb
dpkg -i signalwire-client-c2_*.deb signalwire-client-c2-dev_*.deb
apt-get install -f -y


⚙️ Phase 3: Compiling & Packaging FreeSWITCH

Now that our compilation node is loaded with all custom dependencies, we clone FreeSWITCH and structure our Debian builds.

3.1 Clone the Source Code & Configure

We will target the stable release branch v1.11.1:

Bash
cd /usr/src
git clone -b v1.11.1 --depth 1 https://github.com/signalwire/freeswitch.git /usr/src/freeswitch/src
cd /usr/src/freeswitch/src

./bootstrap.sh -j

# Prevent file system timestamp skew loop
find . -type f -exec touch {} +

./configure \
  --enable-core-odbc-support \
  --enable-core-pgsql-support \
  --enable-pcre2 \
  --prefix=/usr \
  --sysconfdir=/etc \
  --localstatedir=/var \
  --disable-maintainer-mode \
  --disable-dependency-tracking

3.2 Add Custom Workarounds for Debian 13 Packaging

Before launching dpkg-buildpackage, we must bypass several known packaging issues:

  1. Incompatible Modules: Exclude legacy features like V8/Javascript and obsolete audio codecs (silk, ilbc, flite) that are not compatible with Debian 13 build environments.
  2. dh_auto_clean Recursion Loop: Autotools comparisons can trigger an infinite build cleaner recursion loop. We override this loop as a no-op.
  3. Parallel Race Condition: Generating modules.inc under concurrent build jobs (e.g., -j8) results in race conditions. We force sequential module packaging.

Run these adjustments:

Bash
cd /usr/src/freeswitch/src/debian

# Exclude incompatible modules from packaging bootstrap
sed -i '/xml_int\/mod_xml_ldap/a\  languages/mod_v8\n  languages/mod_basic\n  asr_tts/mod_flite\n  codecs/mod_ilbc\n  codecs/mod_silk\n  languages/mod_managed' bootstrap.sh

# Re-run bootstrap to regenerate control and rules definitions
./bootstrap.sh -c trixie

# Prevent auto clean recursive loops in rules
echo "" >> rules
echo "override_dh_auto_clean:" >> rules
printf "\t:\n" >> rules

# Clean up missing dependencies in control metadata
sed -i '/freeswitch-meta-codecs (= ${binary:Version}),/d' control
sed -i '/freeswitch-music,/d' control
sed -i '/freeswitch-sounds,/d' control
sed -i '/freeswitch-mod-flite (= ${binary:Version}),/d' control

Update debian/changelog with the new version metadata:

Bash
cat > changelog <<'EOF'
freeswitch (1.11.1-1) unstable; urgency=medium

  * Repackage FreeSWITCH 1.11.1 for Debian 13.
  * Disable incompatible audio modules and configure make workarounds.

 -- [YOUR_FULL_NAME] <[EMAIL_ADDRESS]>  Tue, 10 Sep 2025 20:30:00 +0000
EOF

3.3 Compile and Isolate

Synchronize all file timestamps to prevent GNU Make from triggering file skew regenerations, then run the sequential build command:

Bash
cd /usr/src/freeswitch/src
find . -type f -exec touch {} +

# Clean out temporary debug assets
fakeroot debian/rules clean
rm -rf debian/python-esl-dbg debian/libfreeswitch1-dbg

# Execute sequential packaging
DEB_BUILD_OPTIONS="parallel=1" dpkg-buildpackage -us -uc -b -d

Once compilation completes, move all created .deb files into the local directory /usr/src/freeswitch/deb/:

Bash
mkdir -p /usr/src/freeswitch/deb
mv /usr/src/freeswitch/*.deb /usr/src/freeswitch/deb/ 2>/dev/null || true
mv /usr/src/freeswitch/libfreeswitch*.deb /usr/src/freeswitch/deb/ 2>/dev/null || true
mv /usr/src/freeswitch/libesl*.deb /usr/src/freeswitch/deb/ 2>/dev/null || true
mv /usr/src/freeswitch/python-esl*.deb /usr/src/freeswitch/deb/ 2>/dev/null || true


🌐 Phase 4: Publishing to the APT Repository Server

Now that our packages are compiled and isolated locally in /usr/src/freeswitch/deb/, we upload them to our repository server (IP: , configured in Part 1).

To upload packages without entering the remote server password every time, configure SSH key-based authentication from your build node to the repository server:

  1. Generate SSH Key on your build node (if you do not have one):
Bash
   ssh-keygen -t rsa -b 4096 -C "build-node"
   # Press Enter to accept defaults (do not set a passphrase for automation)
   

  1. Copy the public key to the repository server:
Bash
      ssh-copy-id -i ~/.ssh/id_rsa.pub root@<YOUR_REPO_IP>
   # Alternatively, using cat:
   # cat ~/.ssh/id_rsa.pub | ssh root@<YOUR_REPO_IP> "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"
   

  1. Verify passwordless access:
Bash
   ssh root@<YOUR_REPO_IP> "echo 'SSH Connection Successful!'"
   

4.2 Upload Packages

From your build node, secure copy the packages directly to the repository upload directory:

Bash
scp /usr/src/freeswitch/deb/*.deb root@76.13.102.153:/var/www/pbx-repo/ring2all/trixie/apt/pool/b/base/
scp /usr/src/freeswitch/deb/*.deb root@<YOUR_REPO_IP>:/var/www/pbx-repo/ring2all/trixie/apt/pool/b/base/

4.3 Sort and Sign on the Repo Server

Log into your repository server over SSH, organize the files into their target component directories, and rebuild the repository signatures:

Bash
ssh root@<YOUR_REPO_IP>

# Sort packages into pool components (base, extras, devel, etc.)
CopyPackagesToRepo

# Rebuild package indexes and sign Release signatures
BuildRepoRing2All

Once completed, the packages are immediately ready for secure deployment.


⏭️ What's Next?

Our repository server now serves FreeSWITCH core engines and custom dependencies over HTTPS. In Part 3: Compiling and Packaging Kamailio on Debian 13, we will complete our setup by packaging the high-performance SIP router Kamailio (v6.1), publishing it, and executing our end-to-end client installation.

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?