For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
CommunitySign Up
HomeGuidesVerbsAPI ReferenceSelf-HostingClient SDKsTutorialsChangelog
HomeGuidesVerbsAPI ReferenceSelf-HostingClient SDKsTutorialsChangelog
  • Overview
    • Licensing
    • Post-Install Steps
    • Setting up WebRTC and SIP TLS
  • Hosting Providers
    • AWS
    • Azure
    • GCP
    • OCI
  • Bare metal / VPS
    • Debian package
  • Kubernetes
    • AWS (EKS)
    • Azure (AKS)
    • GCP (GKE)
    • Exoscale (SKS)
LogoLogo
CommunitySign Up
On this page
  • Prerequisites
  • Required open ports
  • Add the apt sources
  • Choose your portal DNS name
  • Supplying the domain to the installer
  • Install jambonz mini
  • What the postinst did
  • Verify the install
  • Post-install steps
  • First login
  • Logging in as the jambonz user
  • The jambonz CLI
  • Tailing logs
  • Customizing config
  • Files you can edit (conffile-protected)
  • Files that are generated — do NOT edit
  • Adding custom systemd directives
  • Upgrading
  • Conffile prompts
  • Recommended pre-upgrade ritual
  • Rolling back
  • Removing
  • Troubleshooting
  • Known limitations
Bare metal / VPS

Debian package

Install jambonz mini on any Debian 12 host using our apt repository.
Was this page helpful?
Edit this page
Previous

Kubernetes Deployment

Deploy jambonz on Kubernetes
Next
Built with

For bare metal, any VPS provider (Hetzner, Vultr, DigitalOcean, Linode, OVH, on-prem, etc.), or an existing Debian 12 host, jambonz publishes a .deb package that installs the whole single-host stack from apt. This is the simplest deployment path when you’re not on AWS / Azure / GCP / OCI and don’t want to use one of the prebuilt images.

This guide installs the jambonz mini all-in-one deployment (every component on a single host). Multi-host topologies — separate SBC, feature-server, web, and monitoring tiers — currently still require the cloud-image guides (AWS, Azure, GCP, OCI). Multi-host Debian packages are in development.

Prerequisites

  • Debian 12 (bookworm), amd64
  • sudo access
  • At least 4 vCPU, 8 GB RAM, 100 GB disk
  • Public IPv4 address
  • Network rules / firewall configured to allow the ports below

Required open ports

ServicePorts
SIP5060/udp, 5060/tcp, 5061/tcp, 8443/tcp
HTTP / HTTPS80/tcp, 443/tcp
RTP media40000–60000/udp

How you open these depends on your provider — security groups on a cloud VPS, an OS-level firewall (ufw, nftables) on bare metal, or a hardware firewall in front. Consult your provider’s documentation.

Add the apt sources

Four apt sources are required: the jambonz repository itself, plus three upstream sources that ship dependencies Debian doesn’t include directly.

$sudo apt-get update -qq
$sudo apt-get install -y -qq curl ca-certificates gnupg systemd
$
$# 1. jambonz APT source
$sudo install -d /etc/apt/keyrings
$curl -fsSL https://jambonz-debian-packages.s3.us-east-2.amazonaws.com/jambonz.gpg \
> | sudo gpg --dearmor -o /etc/apt/keyrings/jambonz.gpg
$echo "deb [signed-by=/etc/apt/keyrings/jambonz.gpg] https://jambonz-debian-packages.s3.us-east-2.amazonaws.com/debian bookworm main" \
> | sudo tee /etc/apt/sources.list.d/jambonz.list
$
$# 2. InfluxData APT source (for telegraf — not in Debian's repos)
>curl -fsSL https://repos.influxdata.com/influxdata-archive.key \
> | sudo gpg --dearmor -o /etc/apt/keyrings/influxdata.gpg
>echo "deb [signed-by=/etc/apt/keyrings/influxdata.gpg] https://repos.influxdata.com/debian stable main" \
> | sudo tee /etc/apt/sources.list.d/influxdata.list
>
># 3. NodeSource APT source (Node 22 — Debian ships 18)
>curl -fsSL https://deb.nodesource.com/setup_22.x | sudo bash -
>
># 4. Grafana APT source (for the metrics dashboard)
>curl -fsSL https://apt.grafana.com/gpg.key \
> | sudo gpg --dearmor -o /etc/apt/keyrings/grafana.gpg
>echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" \
> | sudo tee /etc/apt/sources.list.d/grafana.list
>
>sudo apt-get update

Choose your portal DNS name

Before installing, you need to decide on the DNS name you’ll use for the jambonz portal (for example portal.example.com). This is required: jambonz uses the portal domain for nginx routing, certbot TLS, and your software license is keyed to it.

Pick a hostname you control DNS for. The installer will configure nginx for that name and these related subdomains:

  • portal.example.com — the admin webapp
  • api.portal.example.com — the REST API
  • grafana.portal.example.com — metrics dashboards

You should create A records for all of the above pointing at your host’s public IP. You can do this before or after the install, but it must be done before you obtain TLS certificates with certbot. See Post-Install Steps for the DNS record list.

Supplying the domain to the installer

You have three options:

  1. Inline on the install command (recommended / simplest):

    $sudo -E JAMBONES_PORTAL_DOMAIN=portal.example.com \
    > DEBIAN_FRONTEND=noninteractive apt-get install -y jambonz-mini

    The -E flag preserves the environment variable through sudo.

  2. Pre-supply via /etc/jambonz/install.conf (good for terraform / ansible / cloud-init):

    $sudo install -d /etc/jambonz
    $echo "JAMBONES_PORTAL_DOMAIN=portal.example.com" | sudo tee /etc/jambonz/install.conf

    Then run the normal install — the postinst will read install.conf automatically.

  3. Configure it after installing, if you forgot:

    $sudo jambonz-configure-domain portal.example.com

    This idempotent helper:

    • Writes the nginx config (vhosts for portal.example.com, api.portal.example.com, grafana.portal.example.com) and reloads nginx
    • Writes JAMBONES_PORTAL_DOMAIN to /var/lib/jambonz/host.env
    • Updates the system_information row in MySQL (required for drachtio’s license validation)
    • Restarts drachtio, freeswitch, and the jambonz Node services so they pick up the new domain

    Safe to re-run any number of times. Same logic the postinst runs with the env var set.

If you install without specifying a domain, the install completes but the postinst prints a NO PORTAL DOMAIN configured warning — license validation will not succeed until you run jambonz-configure-domain.

Install jambonz mini

rtpengine includes a kernel module that DKMS will compile on the host, so the matching Linux headers must be installed first. Then run the install with your portal domain:

$sudo apt-get install -y linux-headers-cloud-amd64 || sudo apt-get install -y linux-headers-$(uname -r)
$sudo -E JAMBONES_PORTAL_DOMAIN=portal.example.com \
> DEBIAN_FRONTEND=noninteractive apt-get install -y jambonz-mini

apt resolves the full dependency tree (jambonz-common, jambonz-monitoring-agent, jambonz-rtpengine, jambonz-rtpengine-dkms, drachtio, jambonz-freeswitch, telegraf, nodejs, MariaDB, Redis, nginx, Grafana).

On some cloud instances, linux-headers-cloud-amd64 will install headers for a newer kernel than the one currently running (because the running kernel image is older than what’s in the repos). DKMS can build the rtpengine module only for kernels whose headers it has, so it builds against the newer one. The postinst detects this and prints a REBOOT RECOMMENDED message at the end of the install. Reboot to use the newer kernel, after which rtpengine will use kernel-mode forwarding. Until you reboot, rtpengine works in userspace-only mode (functional but less efficient).

What the postinst did

While apt was running, the jambonz-common and jambonz-mini post-install scripts:

  • Created the jambonz system user with passwordless sudo and copied the cloud-init user’s SSH authorized_keys so you can ssh jambonz@<host> with the same keypair
  • Generated per-host secrets in /var/lib/jambonz/secrets.env (mode 600 — JWT signing key, MySQL password, Homer/heplify credentials, recording auth)
  • Auto-detected the host’s local subnet and wrote it to /var/lib/jambonz/host.env
  • Waited for MariaDB, then created the jambones database, ran the schema and seed, and applied any pending migrations
  • Wrote systemd drop-ins for drachtio and FreeSWITCH so they pick up the generated DB/Redis credentials
  • Set up PostgreSQL plus the HEP / SIP-capture stack (heplify-server + pcap-server) with a generated heplify role and the homer_data / homer_config databases
  • Configured InfluxDB and Grafana (moved Grafana to port 3010 to avoid colliding with the Node apps on 3000)
  • Configured the upload_recordings C++ daemon with the matching ENCRYPTION_SECRET so it can decrypt bucket credentials
  • Checked the rtpengine DKMS kernel module against the running kernel — building it if headers are available, or queueing a REBOOT RECOMMENDED warning if a newer kernel was installed
  • Wrote the nginx config (vhosts for portal / api / grafana if a portal domain was supplied, otherwise a catch-all on the default server) and reloaded nginx
  • Enabled and started the jambonz-apps.target systemd target and its child services, plus drachtio, FreeSWITCH, rtpengine, heplify-server, pcap-server, InfluxDB, and Grafana

On a clean install this completes in a minute or two.

Verify the install

$echo "=== mysql admin user ==="
$sudo mysql -e "SELECT user, host FROM mysql.user WHERE user='admin';"
$
$echo "=== schema version ==="
$sudo mysql -e "USE jambones; SELECT version FROM schema_version;"
$
$echo "=== telegraf listening on :8125 ==="
$sudo ss -tnlp '( sport = :8125 )'
$
$echo "=== systemd units ==="
$systemctl --no-pager list-units 'jambonz-*' --all
$
$echo "=== webapp returns HTTP 200 ==="
$curl -sI http://localhost/ | head -1
$
$echo "=== no ECONNREFUSED in recent logs (should be 0) ==="
$sudo journalctl -u 'jambonz-*' -n 200 --no-pager 2>/dev/null | grep -c 'ECONNREFUSED.*8125'

All jambonz-* units should be active (running). The webapp should return HTTP/1.1 200 OK. The ECONNREFUSED count should be 0.

Post-install steps

Before logging in for the first time, complete these steps:

  • DNS records and HTTPS — see the Post-Install Steps article. It walks through creating the A records (api. and grafana.) and using certbot to obtain a TLS certificate for the portal.
  • WebRTC and SIP TLS — see Setting up WebRTC and SIP TLS if you need WSS or SIPS.
  • Licensing — obtain a license key as described in the Licensing article. Free trial licenses are available; extended licenses for non-commercial use and pre-revenue startups can be requested at support@jambonz.org.

First login

Browse to your portal’s DNS name (e.g. https://my-domain.example.com) and log in with:

  • Username: admin
  • Password: admin

You’ll be required to set a new password on first login.

Logging in as the jambonz user

The install creates a jambonz system user with passwordless sudo and copies the cloud-init user’s SSH keys into /home/jambonz/.ssh/authorized_keys. Day-2 operations are intended to be performed as this user — not as root, admin, ubuntu, or ec2-user.

$ssh jambonz@<this-host>

You should see this user mentioned in the final lines of the install output. Once logged in, the jambonz CLI is on $PATH and sudo works without a password prompt.

The jambonz CLI

The jambonz-common package ships a jambonz command at /usr/bin/jambonz for day-2 operations. Run it as the jambonz user:

CommandDescription
sudo jambonz upgradeRefresh apt indexes, list which jambonz packages have new versions, prompt for confirmation, run the upgrade, then run jambonz health. No-op when there’s nothing new.
jambonz healthStack-level check: every jambonz Node app, drachtio, FreeSWITCH, rtpengine, upload-recordings, pcap-server, heplify-server, MariaDB, Redis, PostgreSQL, and nginx — are they all active and listening on the right ports?
jambonz versionPrint the installed package versions (apt + bundled Node apps).
jambonz status (alias ls)List jambonz Node apps with state, PID, restart count, last-start age, and memory usage. Flags apps that have restarted.
jambonz logs [-f] [<app>]Tail logs. With no <app>, streams the merged journal of every jambonz Node app. With <app>, scoped to that unit. -f follows in real time.
jambonz show <app>Detailed systemctl status block + the last 30 journal lines + the listening ports owned by that app’s PID.
jambonz env <app>Print the resolved environment that <app> would see (after merging the 4-layer env chain). Useful for debugging config.
jambonz crash <app>Show a bracketed journalctl window — 30s before through 60s after the most recent (re)start of <app>. The fastest way to look at a crash.
jambonz start|stop|restart <app|all>Lifecycle control for one app or every jambonz Node app at once. Auto-elevates via sudo.

Tailing logs

Common usage:

$# Stream every jambonz Node app's journal, live
>jambonz logs -f
>
># Same, scoped to one app (short form — "feature-server" → jambonz-feature-server)
>jambonz logs -f feature-server
>
># Print recent logs and exit (no -f)
>jambonz logs api-server
>
># For non-Node services, pass the full unit name
>jambonz logs -f drachtio
>jambonz logs -f freeswitch

App-name resolution is forgiving: short forms like feature-server, api-server, inbound, outbound are expanded to their jambonz-* unit names. For drachtio, FreeSWITCH, and rtpengine — which aren’t part of the jambonz Node-app set — pass the full unit name.

Customizing config

Environment variables for the Node apps come from four files, layered in order — later files override earlier ones on conflict:

  1. /etc/jambonz/jambonz.env — shared, customer-editable config
  2. /etc/jambonz/<app>.env — per-app overrides (e.g. feature-server.env)
  3. /var/lib/jambonz/host.env — per-host runtime (auto-generated; also the right place for terraform/ansible to write)
  4. /var/lib/jambonz/secrets.env — generated cluster secrets, mode 600

Files you can edit (conffile-protected)

These files are tracked by dpkg. Your edits survive upgrades; if a release ships a new default you’ll be prompted (see Upgrading below).

FilePurpose
/etc/jambonz/jambonz.envShared env for all Node apps (DB host, Redis host, log level)
/etc/jambonz/feature-server.envfeature-server overrides
/etc/jambonz/api-server.envapi-server overrides
/etc/jambonz/inbound.envsbc-inbound overrides
/etc/jambonz/outbound.envsbc-outbound overrides
/etc/jambonz/sbc-call-router.envsbc-call-router overrides
/etc/jambonz/sbc-sip-sidecar.envsbc-sip-sidecar overrides
/etc/jambonz/sbc-rtpengine-sidecar.envsbc-rtpengine-sidecar overrides
/etc/drachtio/drachtio-5070.conf.xmlFS-tier drachtio config
/etc/drachtio.conf.xmlSBC-tier drachtio config
/etc/freeswitch/...FreeSWITCH config
/etc/rtpengine/rtpengine.confrtpengine config

Files that are generated — do NOT edit

These get rewritten on every upgrade. Edits will be lost. If you need to change a value, edit the source env file instead.

FileSource of truth
/etc/nginx/sites-available/jambonzwritten by jambonz-configure-domain — to change vhosts, re-run that script
/etc/default/upload-recordings/var/lib/jambonz/secrets.env + /etc/jambonz/jambonz.env
/etc/systemd/system/drachtio.service.d/jambonz.conf/etc/jambonz/jambonz.env
/etc/systemd/system/drachtio.service.d/jambonz-execstart.confstatic — systemd-252 workaround + HEP wiring
/etc/systemd/system/drachtio-feature-server.service.d/jambonz.conf/etc/jambonz/jambonz.env
/etc/default/drachtio-feature-serverstatic — FS-tier port + config overrides
/etc/systemd/system/freeswitch.service.d/jambonz.conf/etc/jambonz/jambonz.env
/etc/systemd/system/grafana-server.service.d/jambonz.confstatic — port 3010 to avoid collision with feature-server
/etc/sudoers.d/jambonzwritten by jambonz-common.postinst — passwordless sudo for the jambonz user

After editing any of the conffile-protected files above, apply with:

$sudo systemctl restart jambonz-apps.target

Adding custom systemd directives

If you need extra Environment= or ExecStartPre= directives on a system unit (for example to enable SIP over TLS on drachtio), don’t edit the shipped service file or the jambonz*.conf drop-ins — both get reset on upgrade. Create your own drop-in whose name does not start with jambonz:

$sudo install -d -m 755 /etc/systemd/system/drachtio.service.d
$sudo tee /etc/systemd/system/drachtio.service.d/local.conf >/dev/null <<'EOF'
$[Service]
$ExecStartPre=/usr/local/sbin/setup-tls-cert.sh
$Environment="DRACHTIO_EXTRA_ARGS=--sips-port 5061"
$EOF
$
$sudo systemctl daemon-reload
$sudo systemctl restart drachtio

jambonz upgrades will never touch a drop-in not named jambonz*.conf. Your local.conf survives every upgrade.

Upgrading

The simplest path:

$sudo jambonz upgrade

This refreshes apt indexes, lists which jambonz packages have new versions, asks for confirmation, runs the upgrade, and finishes with a jambonz health so you immediately see whether anything came back failing. If there’s nothing new, it’s a no-op with a clean message.

The equivalent raw apt commands:

$sudo apt-get update
$sudo apt-get upgrade

Schema migrations run automatically — the jambonz-mini postinst detects the existing schema_version table, skips the seed, and applies only pending migrations. Existing data is preserved.

Conffile prompts

If an upgrade ships a new default for a config file under /etc/ and you’ve edited that file, dpkg will prompt you:

Configuration file '/etc/jambonz/feature-server.env'
==> Modified (by you or by a script) since installation.
==> Package distributor has shipped an updated version.
What would you like to do about it? Your options are:
Y or I : install the package maintainer's version
N or O : keep your currently-installed version
D : show the differences between the versions
Z : start a shell to examine the situation
The default action is to keep your current version.
*** feature-server.env (Y/I/N/O/D/Z) [default=N] ?

The default (N — keep your edits) is almost always the right answer for env files. Press D first to see what changed — sometimes the new version adds variables worth adopting, in which case keep your file with N and add the new variables manually.

For the two third-party conffiles /etc/heplify-server.toml and /etc/default/pcap-server, always pick N. The jambonz-mini postinst sed-edits credential lines into those files. Picking Y would lose the generated credentials and break the services. If you accidentally pick Y, restore with sudo dpkg-reconfigure jambonz-mini.

Recommended pre-upgrade ritual

For production boxes:

$# 1. Snapshot the database
$sudo mysqldump --single-transaction --routines --triggers jambones \
> > /tmp/jambones-$(date +%F).sql
$
$# 2. Capture current versions for rollback reference
$jambonz version > /tmp/versions-before.txt
$
$# 3. Confirm the system is healthy before upgrading
$jambonz health
$
$# 4. Upgrade
$sudo jambonz upgrade
$
$# 5. Confirm health after (jambonz upgrade does this automatically too)
$jambonz health

Rolling back

If an upgrade breaks something and you need to drop back to a previous version:

$# Show available versions
$apt-cache madison jambonz-mini
$
$# Pin back to a specific version
$sudo apt-get install --allow-downgrades \
> jambonz-mini=10.1.5 \
> jambonz-common=10.1.5

Schema migrations do not auto-roll-back — once new tables or columns exist, they persist. That’s safe because the migration policy guarantees backward compatibility (no DROP TABLE, no new NOT NULL columns without defaults), so old code reading a newer schema works correctly. You don’t need to restore the database to roll back code.

If you ever do need the schema rolled back, restore MySQL from your pre-upgrade backup before downgrading the deb.

Removing

$sudo apt-get purge jambonz-mini jambonz-common jambonz-monitoring-agent

apt-get purge removes configuration files but does not drop the jambones database — your data is preserved. For a true reset, drop it manually:

$sudo mysql -e "DROP DATABASE jambones; DROP USER 'admin'@'%';"

Troubleshooting

A jambonz-* unit shows failed after upgrade

Use jambonz crash <app> to see a bracketed journalctl window around the most recent restart of the failing app. This is usually faster than scrolling through journalctl -xe.

MySQL connection errors from a Node app

Confirm /var/lib/jambonz/secrets.env has the expected password:

$sudo grep JAMBONES_MYSQL_PASSWORD /var/lib/jambonz/secrets.env
$sudo mysql -uroot -e "SELECT user FROM mysql.user WHERE user='admin'\\G"

If the password and the MySQL user are out of sync, re-run sudo dpkg-reconfigure jambonz-mini to regenerate.

Recording 404s after upgrade

Check the feature-server’s recording websocket URL:

$jambonz env feature-server | grep -i record

JAMBONZ_RECORD_WS_BASE_URL should be ws://127.0.0.1:3017. Then confirm the recording service is up with jambonz status — upload_recordings should be active.

pcap retrieval suddenly returns empty

The drachtio HEP-encapsulation drop-in may have been clobbered. Confirm it still exists:

$sudo cat /etc/systemd/system/drachtio.service.d/jambonz-execstart.conf

It should include --homer 127.0.0.1:9060 --homer-id 10. If missing or different, re-run sudo dpkg-reconfigure jambonz-mini.

TLS certificate expired

sudo certbot renew. Certbot installs a systemd timer that handles renewals automatically — only run this manually if you see “Your certificate has expired” in the portal.

Something else is wrong and I don't know where to start

Run jambonz health first. It narrows the problem to a specific layer (database, Redis, telegraf, drachtio, FreeSWITCH, or a Node app) much faster than reading logs unaided.

Known limitations

Multi-host topologies (separate SBC, feature-server, web, and monitoring tiers) are not yet supported via apt. Only the single-host jambonz-mini package is currently published. If you need a clustered deployment, use the AWS, Azure, GCP, OCI, or Kubernetes guides instead.