H7
Observability Azure

HOMER 7 on Ubuntu 24.04 on Azure User Guide

| Product: HOMER 7 on Ubuntu 24.04 LTS on Azure

Overview

This image is a complete HOMER 7 capture platform. Three moving parts are installed and — more importantly — they are already wired to each other:

  • heplify-server 1.60.9 receives HEP (the Homer Encapsulation Protocol) from your SBCs, SIP proxies and media servers, and writes every packet into the capture store.
  • PostgreSQL 16 is that store: a partitioned, automatically rotated schema that is created and ready before you sign in.
  • homer-app 1.5.20, with the HOMER 7 web UI bundled inside it, serves search, ladder diagrams, per hop SIP messages and QoS reports behind a TLS front door.

On a plain install you would create the databases and roles by hand, run the schema migrations, write a heplify-server configuration, wire the web app to both databases, generate a JWT secret and set up TLS. Here all of that is done during the VM's first boot, with secrets generated uniquely for your machine.

The HEP ingest port is deliberately closed until you open it. heplify-server is listening on 9060 (UDP and TCP) exactly as upstream intends, so the capture path works the moment you permit the traffic — but nothing off the box can reach it on a stock deployment. HEP carries no authentication that the server enforces, so restricting the source of the traffic is the security control, and Step 7 walks through opening it to your own signalling hosts and nothing else.

What is included:

  • heplify-server 1.60.9 as the heplify-server systemd service, listening for HEP on 9060/udp and 9060/tcp
  • homer-app 1.5.20 with the bundled HOMER 7 UI (7.11.23) as the homer-app service, bound to 127.0.0.1:9080
  • PostgreSQL 16 holding homer_config and homer_data, bound to 127.0.0.1:5432, on a dedicated 32 GiB Azure data disk
  • A partitioned capture schema with automatic rotation and a 14 day retention window, both tunable
  • nginx terminating TLS on a certificate generated on this VM, with HTTP permanently redirecting to HTTPS
  • A default deny host firewall permitting only SSH, HTTP and HTTPS
  • Five independent secrets generated on first boot — the admin login, the support login, the PostgreSQL password, the JWT signing secret and the TLS key — none of which ship inside the image
  • A self test that sends a synthetic SIP call into this VM's own HEP listener and reads it back through the API
  • An unauthenticated /healthz endpoint for Azure Load Balancer health probes
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet and subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point for a lab or a small production capture; scale the VM and the data disk up with your call volume.

NSG inbound rules: allow 22/tcp from your management network and 443/tcp from wherever you will browse the UI. 80/tcp is optional and only exists to redirect callers to HTTPS. Do not add a 9060 rule yetStep 7 covers that, scoped to your own signalling hosts.

The TLS certificate generated on first boot is self-signed, so browsers will show a warning until you replace it with your own certificate (see Maintenance). The curl examples below therefore use -k.

Step 1 - Deploy from the Azure Marketplace

In the Azure portal, choose Create a resource, search for HOMER 7 on Ubuntu 24.04 LTS by cloudimg, and select Create. Pick your subscription, resource group and region, choose Standard_B2s or larger, and select SSH public key authentication with the username azureuser. On the Networking tab allow inbound 22 and 443. Review and create.

Step 2 - Deploy from the Azure CLI

Replace the resource group, region and image reference to match your subscription.

az group create --name voip-capture-rg --location eastus

az vm create \
  --resource-group voip-capture-rg \
  --name homer7-01 \
  --image <cloudimg-homer-7-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

az vm open-port --resource-group voip-capture-rg --name homer7-01 --port 443 --priority 1001

Step 3 - Connect to your VM

ssh azureuser@<your-vm-ip>

Step 4 - Confirm the services are running

Four services make up the appliance: PostgreSQL (the capture store), heplify-server (the HEP receiver), homer-app (the UI and API) and nginx (the TLS front door).

systemctl is-active postgresql heplify-server homer-app nginx

Expected output:

active
active
active
active

Now look at the network posture, which is the most important thing to understand about this image. heplify-server is listening on 9060, but only 22, 80 and 443 are permitted through the host firewall:

echo "bound off box:"
{ ss -tlnH | awk '{print "tcp/"$4}'; ss -ulnH | awk '{print "udp/"$4}'; } \
  | grep -vE '/(127\.|\[::1\])' | sed -E 's#(tcp|udp)/.*:([0-9]+)$#  \1/\2#' | sort -u
echo "permitted by the firewall:"
sudo ufw status verbose | grep 'ALLOW IN' | sed -E 's/[[:space:]]+ALLOW IN.*//' | sed 's/^/  /' | sort -u

Expected output:

bound off box:
  tcp/22
  tcp/443
  tcp/80
  tcp/9060
  udp/68
  udp/9060
permitted by the firewall:
  22/tcp
  22/tcp (v6)
  443/tcp
  443/tcp (v6)
  80/tcp
  80/tcp (v6)

udp/68 is the DHCP client. The HEP port appears in the first list and not the second: that is the appliance shipping its capture capability switched on and its front door shut.

Terminal output showing homer-app 1.5.20, heplify-server 1.60.9, nginx 1.24.0 and PostgreSQL 16.15 installed with all four services active, the bound off box listeners being tcp 22, 80, 443 and 9060 plus udp 68 and 9060, the loopback only listeners for PostgreSQL 5432 and homer-app 9080, and the ufw status showing a default deny incoming policy that permits only 22, 80 and 443 so that HEP 9060 is listening but not reachable

Step 5 - Retrieve your credentials

Every secret on this VM was generated during its first boot. Nothing here shipped inside the image, and no two deployments share a value. HOMER seeds two built in accounts, admin and support; each gets its own distinct password here, and neither keeps the upstream default.

sudo cat /root/sipcapture-homer-credentials.txt

The file contains both sign-ins and a reminder of how to open the HEP port safely:

HOMER_URL=https://<your-vm>/
HOMER_USERNAME=admin
HOMER_ADMIN_PASSWORD=<generated on this VM>
HOMER_SUPPORT_USERNAME=support
HOMER_SUPPORT_PASSWORD=<generated on this VM>
HEP_PORT=9060
HEP_STATUS=listening on this VM, closed at the firewall until you open it

Terminal output showing the credentials file is mode 0600 owned by root, its contents listing the HOMER URL, the admin and support usernames with both passwords redacted and the HEP port status line, then a check proving the upstream default password sipcapture returns HTTP 401 for both the admin and support accounts, followed by the per VM TLS certificate subject and validity dates

Confirm the TLS front door is answering and that plain HTTP redirects to it:

curl -sk -o /dev/null -w 'healthz: HTTP %{http_code}\n' https://127.0.0.1/healthz
curl -sk -o /dev/null -w 'port 80: HTTP %{http_code}\n' http://127.0.0.1/

Expected output:

healthz: HTTP 200
port 80: HTTP 301

/healthz is deliberately unauthenticated so an Azure Load Balancer health probe can use it. Everything else requires a login.

Step 6 - Sign in to the web UI

Open https://<your-vm-ip>/ in a browser. Accept the certificate warning (the certificate was generated on your VM and is not signed by a public authority — see Maintenance to replace it). Sign in with admin and the HOMER_ADMIN_PASSWORD from Step 5.

The HOMER sign in page served over HTTPS, showing the HOMER diamond logo above a Homer Login heading with Username and Password fields and a Login button

Step 7 - Enable HEP ingest from your SBC

This is the one deliberate piece of configuration this appliance leaves to you, and it is worth understanding why.

heplify-server is already listening on 9060/udp and 9060/tcp. It is not reachable from off the box because the port is absent from the image's firewall policy and from the network security group the offer creates. HEP has no authentication that heplify-server enforces. The protocol carries an optional authenticate-key chunk, but the server parses it and stores it with the packet rather than rejecting anything on it — there is no capture password to set. Anyone who can reach an open HEP port can therefore write arbitrary call records into your capture store.

So the security control is restricting the source of the traffic, and that is what the two rules below do. Open the port to your own SBC, SIP proxy or media server, and to nothing else.

Replace 203.0.113.10 with the address or prefix of your signalling host in both commands. Run the first from a machine with the Azure CLI, and the second on the VM itself.

az network nsg rule create \
  --resource-group voip-capture-rg \
  --nsg-name homer7-01NSG \
  --name allow-hep-from-sbc \
  --priority 500 \
  --direction Inbound --access Allow --protocol '*' \
  --source-address-prefixes 203.0.113.10/32 \
  --destination-port-ranges 9060
sudo ufw allow from 203.0.113.10 to any port 9060 proto udp
sudo ufw allow from 203.0.113.10 to any port 9060 proto tcp
sudo ufw status verbose

Repeat both for each signalling host, or use a prefix (203.0.113.0/24) if they share one.

Never open 9060 to 0.0.0.0/0, to Any, or to the Azure Internet service tag. An open HEP port is an unauthenticated write endpoint into your capture database.

If you would rather have cryptographic peer authentication than source-IP scoping, heplify-server also speaks HEP over mutual TLS. Set HEPTLSAddr, TLSCertFile, TLSKeyFile, TLSClientCAFile and TLSRequireClientCert = true in /etc/heplify-server.toml, restart the service, and point your agents at the TLS listener — see the heplify-server documentation for the current option names. Note that HEPTLSAddr and HEPTCPAddr cannot both use port 9060; give the TLS listener its own port and open that one instead. Even then, keeping the source-IP rule in place is worthwhile defence in depth.

Step 8 - Point your capture agents at it

Each of these tells the signalling element to duplicate its SIP traffic to this VM as HEP. Use your VM's private address if the agent is in the same VNet, and its public address otherwise.

Kamailio (siptrace with HEP):

loadmodule "siptrace.so"
modparam("siptrace", "duplicate_uri", "sip:<your-vm-ip>:9060")
modparam("siptrace", "hep_mode_on", 1)
modparam("siptrace", "hep_version", 3)
modparam("siptrace", "trace_to_database", 0)
modparam("siptrace", "trace_on", 1)

Asterisk (res_hep, in hep.conf):

[general]
enabled = yes
capture_address = <your-vm-ip>:9060
capture_id = 1234
uuid_type = call-id

FreeSWITCH — load mod_sofia's HEP capture with capture-server set to udp:<your-vm-ip>:9060 in sofia.conf.xml, or run the heplify agent alongside it.

OpenSIPS (proto_hep plus siptrace):

loadmodule "proto_hep.so"
loadmodule "siptrace.so"
modparam("siptrace", "trace_id", "[hep]uri=hep:<your-vm-ip>:9060;version=3")

Anything else — run heplify, the lightweight capture agent, on the signalling host: heplify -i eth0 -m SIP -hs <your-vm-ip>:9060.

Step 9 - Watch calls arrive

Sign in and the dashboard's CALL SIP SEARCH widget lists what has been captured. Set a time range in the top bar, press Search, and each SIP message appears with its method, from and to users, source and destination addresses, and the session ID (the Call-ID). Messages belonging to the same call share a row colour.

The HOMER 7 dashboard after sign in, showing the CALL SIP SEARCH widget on the left with SIP From user, SIP To user, SIP Method and CallID fields, and the Widget Result grid on the right listing captured SIP messages grouped by call with columns for date, session ID, SIP method, from and to user, source and destination IP and ports, including INVITE, 100, 180, 200, ACK and BYE messages

You can prove the whole path from the shell without waiting for real traffic. The self test sends a synthetic SIP call into this VM's own HEP listener, searches it back out through the API, and then deletes its own rows — it only ever sends to 127.0.0.1, so it never touches traffic that is not its own:

sudo /usr/local/sbin/sipcapture-homer-selftest.sh

Expected output:

capture round trip OK: cloudimg-selftest-<random> was captured, searched and purged

Terminal output of the HOMER self test showing a synthetic call being sent to the local HEP listener, retrieved back through the search API and then purged, followed by the last heplify-server journal lines confirming the postgres connection is established and the capture tables are in place

Step 10 - Read a call's ladder diagram

Double click any row in the result grid. HOMER opens that Call-ID with tabs for Message, Flow, Session Info, Logs and Export, and lands on Flow — the ladder diagram. Each participant is a vertical lane, each SIP message an arrow between them, annotated with the method or response code, the transport, the timestamp and the delta from the previous message. Export gives you the same call as a PCAP or as text.

The HOMER 7 call flow ladder diagram for a captured call, showing the two endpoints 10.10.0.11 and 10.10.0.22 as vertical lanes on port 5060 with arrows between them for INVITE with SDP, 100, 180, 200 with SDP and ACK, each arrow labelled with its SIP request line, transport, timestamp and delta

Step 11 - Manage users

Choose the sliders icon at the top left to open Admin Settings. USERS lists the two accounts created at first boot: admin and support, each with its own per VM password. Add your own users here, change passwords, or delete the support account if you do not want it.

The HOMER 7 Admin Settings page with the USERS section selected, showing the settings navigation for Profile, Users, User Settings, Alias, Advanced, Mapping, Hepsub, Auth Token, Agentsub, Reset and API Documentation, and a Users table listing the Homer Admin admin account and the Homer Support support account with edit and delete actions

You can do the same from the API. Sign in for a session token, then list the accounts:

ADMIN_PW=$(sudo grep '^HOMER_ADMIN_PASSWORD=' /root/sipcapture-homer-credentials.txt | cut -d= -f2-)
TOKEN=$(curl -sk -X POST -H 'Content-Type: application/json' \
  -d "{\"username\":\"admin\",\"password\":\"${ADMIN_PW}\"}" \
  https://127.0.0.1/api/v3/auth | python3 -c 'import json,sys; print(json.load(sys.stdin).get("token",""))')
curl -sk -H "Authorization: Bearer ${TOKEN}" https://127.0.0.1/api/v3/users \
  | python3 -c 'import json,sys; [print(u["username"], "-", u["usergroup"]) for u in json.load(sys.stdin)["data"]]'

(Reading the password out of the credentials file keeps it off your shell history. Run this on the VM itself; from elsewhere, substitute the HOMER_ADMIN_PASSWORD value from Step 5.)

Expected output:

admin - admin
support - admin

Step 12 - Where the captures live, and for how long

The capture store is PostgreSQL on a dedicated Azure data disk mounted at /var/lib/postgresql, so it is sized and scaled independently of the operating system disk. heplify-server creates date partitions ahead of time and drops them once they age out.

df -h /var/lib/postgresql | tail -1
sudo grep -E '^DB(Rotate|PartSip|DropDays|UsageProtection|PercentageUsage)' /etc/heplify-server.toml

Expected output:

/dev/sdc         32G  ...  /var/lib/postgresql
DBRotate              = true
DBPartSip             = "1h"
DBDropDays            = 14
DBUsageProtection     = true
DBPercentageUsage     = "80%"

DBDropDays is the retention window in days — raise it to keep captures longer (and grow the data disk to match), lower it to store less. DBUsageProtection stops heplify-server writing once the disk passes DBPercentageUsage, so a burst of traffic cannot fill the volume. Edit /etc/heplify-server.toml and sudo systemctl restart heplify-server to apply a change.

Terminal output showing the dedicated 32 gigabyte data disk mounted at /var/lib/postgresql, the heplify-server rotation settings with DBRotate true, one hour SIP partitions, a 14 day drop window and 80 percent usage protection, a listing of the partitioned hep_proto_1_call parent table with its date partitions, and confirmation that the homer_user database role is not a superuser and cannot create roles or databases

Maintenance

Replace the TLS certificate. The certificate generated at first boot is self-signed. To use your own, copy the certificate and key onto the VM and reload nginx:

sudo cp your-cert.pem /etc/ssl/cloudimg/sipcapture-homer.crt
sudo cp your-key.pem  /etc/ssl/cloudimg/sipcapture-homer.key
sudo chown root:www-data /etc/ssl/cloudimg/sipcapture-homer.key
sudo chmod 640 /etc/ssl/cloudimg/sipcapture-homer.key
sudo nginx -t && sudo systemctl reload nginx

Change a password. From the UI, Admin Settings → USERS → edit. From the shell:

sudo /usr/local/bin/homer-app -update-ui-user admin -update-ui-password 'your-new-password'

Back up the capture store. sudo -u postgres pg_dump homer_config captures your users, dashboards and settings; homer_data holds the packets themselves and is usually recreated rather than restored.

Operating system updates are delivered by Ubuntu's unattended-upgrades, which is enabled in the image.

Service logs:

sudo journalctl -u heplify-server -n 20 --no-pager -o cat | tail -5

Troubleshooting

No calls appear after pointing an agent at the VM. Check the port is open from the agent's side first — nc -vzu <your-vm-ip> 9060 from the signalling host — then confirm both the NSG rule and the ufw rule from Step 7 name that host's address. sudo journalctl -u heplify-server -f shows packets as they arrive.

The UI loads but a search returns nothing. Widen the time range in the top bar: HOMER searches the window shown there, and the default is narrow. Confirm data is landing with:

sudo -u postgres psql -d homer_data -qtAc "SELECT count(*) FROM hep_proto_1_call"

A service will not start. Both product services are gated on their rendered configuration existing, which first boot creates. If systemctl status homer-app reports the condition failed, check that first boot completed:

sudo systemctl status sipcapture-homer-firstboot.service --no-pager | head -5

Certificate warnings in the browser are expected until you install your own certificate — see Maintenance.

Security notes

  • The only ports reachable from off the box on a stock deployment are 22, 80 (which redirects) and 443. The HEP port is listening but firewalled until you open it to a specific source.
  • HOMER's UI and API are never exposed directly; nginx is the only network gate and homer-app is bound to loopback.
  • PostgreSQL listens on loopback only, with TLS disabled because nothing off-host connects to it. The role both services use is not a superuser and cannot create roles or databases.
  • Both built in HOMER accounts are seeded with passwords generated on your VM's first boot. The upstream default is never written, and the demo API token HOMER seeds is deleted.
  • The JWT signing secret is generated on your VM. An empty signing key would let anyone forge a session token, so the appliance refuses to start the web application without one.
  • The image ships the capture schema and the retention policy only. There is no call data of any kind inside it.

About this image

cloudimg builds and maintains this image. HOMER, heplify-server and homer-app are open source projects released under the AGPL-3.0 licence by the SIPCAPTURE community; the software is shipped unmodified and its source is available at github.com/sipcapture. PostgreSQL is the Ubuntu distribution package.

"HOMER" and "SIPCAPTURE" are used here only to identify the software this image contains. cloudimg is not affiliated with, endorsed by or sponsored by the HOMER or SIPCAPTURE projects or their maintainers.

Support: support@cloudimg.co.uk