PJSIP Configuration from Scratch
PJSIP Configuration from Scratch
PJSIP is how modern Asterisk handles SIP. It replaced the older chan_sip module and works differently enough that configs from pre-Asterisk 12 tutorials will not work. This covers the full setup: transport, endpoints, templates, and a SIP trunk to a provider.
On this page
How PJSIP is structured
PJSIP uses a multi-object model. Where chan_sip had one [peer] block that handled everything, PJSIP splits the work across several object types that reference each other:
| Object | What it does |
|---|---|
| transport | Defines the listening IP, port, and protocol (UDP, TCP, TLS) |
| endpoint | Call settings for a phone or trunk (codecs, context, caller ID) |
| auth | Username and password for authentication |
| aor | Address of Record. Where the device registers and how many contacts it can have |
| registration | Outbound registration to a SIP provider |
| identify | Match incoming traffic by IP instead of authentication |
It is more work than chan_sip for a two-phone setup. It pays off when you have 50 endpoints and need to change a codec on all of them (templates) or run multiple transports on different interfaces.
Back up your configuration files
Before editing any Asterisk configuration file, make a backup copy:
bash
sudo cp /etc/asterisk/pjsip.conf /etc/asterisk/pjsip.conf.bak.$(date +%s)
If a typo prevents Asterisk from reloading, you can restore the working version immediately.
Transport
The transport tells Asterisk where to listen for SIP traffic. Most setups need one:
[transport-udp]
type = transport
protocol = udp
bind = 0.0.0.0:5060
If you also need TLS (for WebRTC or encrypted calling):
[transport-tls]
type = transport
protocol = tls
bind = 0.0.0.0:5061
cert_file = /etc/asterisk/keys/asterisk.pem
priv_key_file = /etc/asterisk/keys/asterisk.key
method = tlsv1_2
TLS version
tlsv1_2 requests TLS 1.2. Whether TLS 1.3 clients can negotiate up depends on your PJSIP and OpenSSL build. To explicitly require TLS 1.3 on Asterisk 18+ with a recent PJSIP/OpenSSL, use tlsv1_3. The accepted options are sslv2, sslv3, sslv23, tlsv1, tlsv1_1, tlsv1_2, and, on modern builds, tlsv1_3.
Any endpoint or registration that should use this TLS transport must set transport = transport-tls. The examples below use the default UDP transport unless noted.
If Asterisk is behind NAT
The endpoint-level NAT settings (direct_media = no, rtp_symmetric, etc.) handle phones that are behind NAT routers. They do not help when Asterisk itself is behind NAT, which is common on cloud VMs. For that case, add three options to your transport:
[transport-udp]
type = transport
protocol = udp
bind = 0.0.0.0:5060
local_net = 192.168.0.0/24 ; your local network range
external_signaling_address = 203.0.113.1 ; your public IP
external_media_address = 203.0.113.1 ; your public IP
Without these, Asterisk advertises its private IP in SIP headers and SDP, and remote parties cannot reach it for either signaling or audio. If your server has a public IP directly on the interface, you do not need these.
You cannot reload transports without restarting Asterisk entirely. Changing a transport requires core restart now, not pjsip reload. Get your transports right before deploying.
A single endpoint
The simplest working endpoint has three objects: endpoint, auth, and aor.
[200]
type = endpoint
context = internal
disallow = all
allow = ulaw,alaw
auth = 200-auth
aors = 200
callerid = "Front Desk" <200>
[200-auth]
type = auth
auth_type = userpass
username = 200
password = Kj8#mP2x!qR5
[200]
type = aor
max_contacts = 1
The endpoint's aors line points to the aor by name. The auth line points to the auth. The names can be anything, but using the extension number keeps things readable.
max_contacts = 1 means only one device can register as extension 200 at a time. If someone logs in from a second phone, the first registration gets replaced. Set it higher if you want multiple devices ringing simultaneously (desk phone plus softphone, for example).
disallow = all followed by allow = ulaw,alaw forces the codec negotiation to only accept G.711 variants. Without this, Asterisk offers every codec it knows about, which can cause interoperability issues with some phones.
Templates
When you have more than a few endpoints, repeating the same codec, transport, and context settings on each one gets tedious and fragile. Templates let you define the common settings once:
[office-phone](!)
type = endpoint
context = internal
disallow = all
allow = ulaw,alaw
direct_media = no
rtp_symmetric = yes
force_rport = yes
rewrite_contact = yes
[200](office-phone)
auth = 200-auth
aors = 200
callerid = "Front Desk" <200>
[200-auth]
type = auth
auth_type = userpass
username = 200
password = your_password_here
[200]
type = aor
max_contacts = 1
[201](office-phone)
auth = 201-auth
aors = 201
callerid = "Back Office" <201>
[201-auth]
type = auth
auth_type = userpass
username = 201
password = your_password_here
[201]
type = aor
max_contacts = 1
The (!) after office-phone marks it as a template. It is not a real endpoint; no phone can register as "office-phone." The (office-phone) on endpoints 200 and 201 means "inherit everything from that template." Each endpoint only needs to specify what is unique to it: its auth, aor, and caller ID.
If you later need to change all phones from ulaw,alaw to opus,ulaw, you change one line in the template.
The NAT-related settings in this template (direct_media = no, rtp_symmetric = yes, force_rport = yes, rewrite_contact = yes) are safe defaults for most deployments. They handle the common case where phones are behind NAT routers. On a local network they rarely cause problems, although rewrite_contact = yes instructs Asterisk to store the Contact URI exactly as the phone reports it rather than what appears in the REGISTER request. This is usually what you want behind NAT, but it can affect direct phone-to-phone features and BLF subscriptions if the reported contact is not otherwise routable.
SIP trunk to a provider
A SIP trunk connects Asterisk to the phone network through a VoIP provider. It needs five objects: auth, registration, endpoint, aor, and identify.
; -- Credentials for the provider
[my-provider-auth]
type = auth
auth_type = userpass
username = your_account_id
password = your_account_password
; -- Register with the provider so they know where to send calls
[my-provider-reg]
type = registration
outbound_auth = my-provider-auth
server_uri = sip:sip.provider.com
client_uri = sip:your_account_id@sip.provider.com
retry_interval = 60
expiration = 3600
; -- Endpoint for calls to/from the provider
[my-provider]
type = endpoint
context = from-trunk
disallow = all
allow = ulaw,alaw
outbound_auth = my-provider-auth
aors = my-provider-aor
from_user = your_account_id
from_domain = sip.provider.com
; -- Where the provider lives
[my-provider-aor]
type = aor
contact = sip:sip.provider.com
; -- Match inbound traffic from the provider by IP
[my-provider-identify]
type = identify
endpoint = my-provider
match = 203.0.113.10
match = 203.0.113.11
If you created the TLS transport earlier and the provider supports it, add transport = transport-tls to both the endpoint and registration sections.
The identify section is important. Without it, Asterisk does not know which endpoint to associate with incoming calls from the provider. You tell it "traffic from these IPs belongs to the my-provider endpoint." Get the IPs from your provider's documentation.
The registration section keeps Asterisk registered with the provider. expiration is the requested registration lifetime in seconds; the provider may grant a shorter interval than what you request. retry_interval is how often Asterisk retries after a failure. If Asterisk restarts or the connection drops, it re-registers automatically.
The context = from-trunk on the endpoint sends all inbound calls from this provider to the [from-trunk] context in your dialplan. Never set this to internal or any context with outbound dialing access.
Outbound dialing through the trunk
The dialplan needs a way to route calls out through the trunk:
[internal]
; Outbound: dial 9 + 10-digit number
exten => _9NXXNXXXXXX,1,Set(CALLERID(num)=5551234567)
same => n,Dial(PJSIP/my-provider/${EXTEN:1})
same => n,Hangup()
PJSIP/my-provider/${EXTEN:1} dials the number (with the leading 9 stripped) through the my-provider endpoint. The Set(CALLERID(num)) line sets your outbound caller ID to whatever your provider expects.
Verifying the configuration
After editing pjsip.conf, reload and check:
asterisk -rx "pjsip reload"
Check that endpoints are loaded:
asterisk -rx "pjsip show endpoints"
You should see your endpoints listed with their AOR status. If an endpoint is missing, there is a syntax error in its config block. Check the Asterisk log:
asterisk -rx "pjsip show endpoint 200"
This dumps every setting for endpoint 200, including inherited template values. Useful for confirming that template inheritance worked the way you expected.
For the trunk, check registration:
asterisk -rx "pjsip show registrations"
You want to see Registered in the state column. Rejected usually means wrong credentials. Unregistered with retries means Asterisk cannot reach the provider (DNS, firewall, or wrong server URI).
Common problems
No audio on calls. NAT. Check that your endpoint or template has direct_media = no, rtp_symmetric = yes, force_rport = yes, and rewrite_contact = yes. Also check that SIP signaling ports (UDP 5060, and TCP/TLS 5061 if you use TLS) plus the RTP media range, typically UDP ports 10000-20000 (or whatever your rtp.conf range is), are open in the firewall. If Asterisk itself is behind NAT, also verify that external_signaling_address and external_media_address are set on the transport (see above).
"Unable to create PJSIP channel" on outbound calls. The endpoint name in Dial(PJSIP/my-provider/...) does not match anything configured, or the AOR has no contact. Run pjsip show endpoint my-provider and pjsip show aor my-provider-aor.
Inbound trunk calls rejected or landing in the wrong place. Verify context on the trunk endpoint. Then check the identify section has the correct provider IPs. Without a match, Asterisk cannot associate the incoming traffic with an endpoint.
Registration keeps failing. Turn on SIP logging with pjsip set logger on and watch the exchange. The 401/403 response from the provider usually says exactly what is wrong.
One-way audio after transfers. direct_media = no on all endpoints. Direct media tries to send RTP between phones without Asterisk in the middle, which breaks the moment NAT is involved.
What to read next
- PJSIP Endpoint Templates for a more detailed template example
- PJSIP SIP Trunk Configuration for trunk-specific options
- PJSIP IP ACL for restricting access by IP
- TLS Certificates for PJSIP for encrypted SIP
- res_pjsip module reference for every configuration option
User Notes
Know a tip or gotcha for this topic? Share it below and help others.
Contribute a note
Share a tip, gotcha, or practical example. Keep it under 2000 characters. No questions (use the Asterisk community forums for support). Wrap code in backticks.