HOMER 7 on Ubuntu 24.04 on Azure User Guide
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-serversystemd service, listening for HEP on9060/udpand9060/tcp - homer-app 1.5.20 with the bundled HOMER 7 UI (7.11.23) as the
homer-appservice, bound to127.0.0.1:9080 - PostgreSQL 16 holding
homer_configandhomer_data, bound to127.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
adminlogin, thesupportlogin, 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
/healthzendpoint 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 yet — Step 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.

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

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.

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
9060to0.0.0.0/0, toAny, or to the AzureInternetservice 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.

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

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.

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.

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.

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) and443. 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