Skip to main content

Running behind NAT

SIP puts IP addresses inside its payload, so any address translation between two endpoints has to be accounted for somewhere. Lyno handles the common case automatically and needs two settings for the uncommon one.

Phones behind NAT — nothing to do

Endpoints are dialled at the source address observed during REGISTER, not the address advertised in their Contact header. A phone on a home router advertising 192.168.1.42 is still reachable, because Lyno remembers where the registration actually came from.

This is why a softphone on a mobile network works without any STUN or TURN configuration on the PBX side.

The PBX behind NAT — two settings

When Lyno itself sits behind NAT — a cloud VM with a mapped public address, or Docker bridge networking — it must advertise the address the outside world sees rather than the one it is bound to:

configVersion: lyno/v1
kind: System
spec:
sip:
bindHost: 0.0.0.0 # what to listen on
bindPort: 5060
externalHost: 203.0.113.10 # what to advertise in SIP
media:
externalIp: 203.0.113.10 # what to advertise in SDP
rtpPortMin: 10000
rtpPortMax: 20000
SettingAffectsSymptom when missing
sip.externalHostContact and Via headers in signallingCalls set up and then time out; the far end sends to a private address
media.externalIpThe connection address in SDPSignalling works, audio is one-way or absent

Both take a host — an IP address, or a hostname that resolves to the public address.

Ports to open

PortProtocolPurpose
sip.bindPort (default 5060)UDP, and TCP if enabledSIP signalling
media.rtpPortMinrtpPortMaxUDPRTP audio

The RTP range must be forwarded one-to-one — no port rewriting. Each call leg uses a couple of ports, so size the range for peak concurrency: a range of 200 ports comfortably covers a busy small deployment, and the default 10000–20000 covers anything.

Setting only one of rtpPortMin / rtpPortMax is a validation error, as is a maximum that is not greater than the minimum.

Carrier-side considerations

Trunk source addresses are what authorise inbound calls, so the firewall in front of the PBX should allow SIP from your carrier's signalling addresses and RTP from their media addresses. Those are often different ranges — check the carrier's documentation rather than assuming.

The INBOUND FROM column on the dashboard's Trunks tab lists the addresses Lyno will accept a trunk call from. If a carrier sends from an address that is not there, the call is treated as an unauthenticated endpoint call and refused. Add the address to the trunk's inbound.matchIps.

Diagnosing

SymptomLikely cause
Phone registers, calls to it never ringFirewall dropping SIP from the PBX to the phone, or a NAT mapping that expired — lower the phone's registration interval
Calls connect, no audio either waymedia.externalIp unset, or the RTP range not forwarded
Calls connect, audio one way onlyRTP forwarded in one direction only, or a codec mismatch — check the logs for a bridge error
Inbound calls rejected, outbound fineThe carrier's source address is not in INBOUND FROM
Everything works locally, nothing works from outsidesip.externalHost unset

Run with -debug and watch the Logs tab while placing a call; the addresses being advertised appear there.