Debian package
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
sudoaccess- 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
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.
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 webappapi.portal.example.com— the REST APIgrafana.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:
-
Inline on the install command (recommended / simplest):
The
-Eflag preserves the environment variable throughsudo. -
Pre-supply via
/etc/jambonz/install.conf(good for terraform / ansible / cloud-init):Then run the normal install — the postinst will read
install.confautomatically. -
Configure it after installing, if you forgot:
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_DOMAINto/var/lib/jambonz/host.env - Updates the
system_informationrow 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.
- Writes the nginx config (vhosts for
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:
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
jambonzsystem user with passwordless sudo and copied the cloud-init user’s SSHauthorized_keysso you canssh jambonz@<host>with the same keypair - Generated per-host secrets in
/var/lib/jambonz/secrets.env(mode600— 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
jambonesdatabase, 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 generatedheplifyrole and thehomer_data/homer_configdatabases - Configured InfluxDB and Grafana (moved Grafana to port 3010 to avoid colliding with the Node apps on 3000)
- Configured the
upload_recordingsC++ daemon with the matchingENCRYPTION_SECRETso 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.targetsystemd 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
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.andgrafana.) and usingcertbotto 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.
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:
Tailing logs
Common usage:
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:
/etc/jambonz/jambonz.env— shared, customer-editable config/etc/jambonz/<app>.env— per-app overrides (e.g.feature-server.env)/var/lib/jambonz/host.env— per-host runtime (auto-generated; also the right place for terraform/ansible to write)/var/lib/jambonz/secrets.env— generated cluster secrets, mode600
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).
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.
After editing any of the conffile-protected files above, apply with:
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:
jambonz upgrades will never touch a drop-in not named jambonz*.conf. Your local.conf survives every upgrade.
Upgrading
The simplest path:
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:
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:
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:
Rolling back
If an upgrade breaks something and you need to drop back to a previous version:
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
apt-get purge removes configuration files but does not drop the jambones database — your data is preserved. For a true reset, drop it manually:
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:
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_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:
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.