Migrating from chan_sip to PJSIP
Migrating from chan_sip to PJSIP
chan_sip and chan_pjsip handle SIP differently at the object level, so there is no clean 1:1 mapping between them. chan_sip compressed everything into a single [peer] block: auth, location, codecs, NAT settings, all of it. PJSIP splits those responsibilities across separate objects (endpoint, auth, aor, registration, identify) that reference each other by name. You write more config upfront. It pays off when you have fifty endpoints and need to change one setting across all of them.
If you are coming from chan_sip for the first time, start with the conversion script to get a working base, then compare the examples below to understand what changed and why.
On this page
The conversion script
Asterisk ships a Python script that converts sip.conf to pjsip.conf. It handles the common cases, not every edge case. Treat the output as a draft, not a finished config.
The script is in the Asterisk source tree:
contrib/scripts/sip_to_pjsip/sip_to_pjsip.py
Run it from /etc/asterisk so it finds any #include'd files:
cd /etc/asterisk
/path/to/asterisk/source/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py
Output:
Reading sip.conf
Converting to PJSIP...
Writing pjsip.conf
Default input is sip.conf, default output is pjsip.conf. Included files are converted with a pjsip_ prefix. Use --prefix=xxx to change that.
PJSIP syntax is stricter than chan_sip
Extra spaces around =, unexpected capitalization, or missing required objects will cause silent failures or log warnings. If something does not load after conversion, check /var/log/asterisk/messages.
Side-by-side configuration examples
Endpoint configuration (phones)
Two phones registered to Asterisk with password authentication. Phone 6001 uses dynamic registration; 6002 has a static IP.
sip.conf:
[general]
udpbindaddr=0.0.0.0
[6001]
type=friend
host=dynamic
disallow=all
allow=ulaw
context=internal
secret=1234
[6002]
type=friend
host=192.0.2.1
disallow=all
allow=ulaw
context=internal
secret=1234
pjsip.conf:
[simpletrans]
type=transport
protocol=udp
bind=0.0.0.0
[6001]
type=endpoint
context=internal
disallow=all
allow=ulaw
aors=6001
auth=auth6001
[6001]
type=aor
max_contacts=1
[auth6001]
type=auth
auth_type=userpass
password=1234
username=6001
[6002]
type=endpoint
context=internal
disallow=all
allow=ulaw
aors=6002
auth=auth6002
[6002]
type=aor
contact=sip:6002@192.0.2.1:5060
[auth6002]
type=auth
auth_type=userpass
password=1234
username=6002
The PJSIP config is longer because chan_sip's type=friend block breaks into three separate objects: endpoint, aor, and auth. At two phones it looks like extra work. At fifty phones with templates, you change one line instead of fifty.
SIP trunk configuration
A provider trunk: register outbound, authenticate on outbound calls, accept inbound from the provider's IP.
sip.conf:
[general]
udpbindaddr=0.0.0.0
register => myaccountname:1234567890@203.0.113.1:5060
[mytrunk]
type=friend
secret=1234567890
username=myaccountname
host=203.0.113.1
disallow=all
allow=ulaw
context=from-external
pjsip.conf:
[simpletrans]
type=transport
protocol=udp
bind=0.0.0.0
[mytrunk]
type=registration
outbound_auth=mytrunk
server_uri=sip:myaccountname@203.0.113.1:5060
client_uri=sip:myaccountname@203.0.113.1:5060
[mytrunk]
type=auth
auth_type=userpass
password=1234567890
username=myaccountname
[mytrunk]
type=aor
contact=sip:203.0.113.1:5060
[mytrunk]
type=endpoint
context=from-external
disallow=all
allow=ulaw
outbound_auth=mytrunk
aors=mytrunk
[mytrunk]
type=identify
endpoint=mytrunk
match=203.0.113.1
The identify object is the part people miss first. In chan_sip, host=203.0.113.1 handled both outbound dialing and inbound matching. In PJSIP those are separate: the AOR handles where to send calls, and identify maps inbound traffic from that IP to the endpoint. Without it, Asterisk receives the call but cannot tell which endpoint it belongs to.
NAT settings
chan_sip used a single nat= value per peer. PJSIP breaks NAT behavior into three endpoint options you can set independently.
chan_sip nat= values (Asterisk 1.8+):
| nat= | Description |
|---|---|
no |
No NAT handling beyond RFC 3581 |
force_rport |
Always send responses to the source address and port, even without rport in the Via header |
comedia |
Send media to the address and port Asterisk received it from, ignoring SDP |
auto_force_rport |
Automatically enable force_rport if NAT is detected (default) |
auto_comedia |
Automatically enable comedia if NAT is detected |
Equivalent PJSIP endpoint options:
| chan_sip | pjsip.conf |
|---|---|
nat=yes |
rtp_symmetric=yes, force_rport=yes, rewrite_contact=yes |
nat=no |
rtp_symmetric=no, force_rport=no, rewrite_contact=no |
nat=never |
rtp_symmetric=no, force_rport=no, rewrite_contact=no |
nat=route |
force_rport=yes, rewrite_contact=yes (rtp_symmetric stays no) |
The three options break down like this:
rtp_symmetric-- send RTP back to the address and port Asterisk received audio from, regardless of what the SDP says (chan_sip called thiscomedia)force_rport-- always send SIP responses to the source port, even if the Via header says otherwise (same name in chan_sip)rewrite_contact-- rewrite the SIP Contact to the actual source address of the request so subsequent SIP messages go to the right place (no direct chan_sip equivalent; bundled implicitly intonat=yesandnat=route)
Running chan_sip and PJSIP together during migration
You do not have to cut over all at once. Disable PJSIP until you are ready by adding these lines to modules.conf:
noload => res_pjsip.so
noload => res_pjsip_pubsub.so
noload => res_pjsip_session.so
noload => chan_pjsip.so
noload => res_pjsip_exten_state.so
noload => res_pjsip_log_forwarder.so
Restart Asterisk after editing. Remove the noload lines when you are ready to switch.
To run both at the same time on different ports, change the bind port in the PJSIP transport (e.g., chan_sip on 5060, PJSIP on 5061). Point test phones at the PJSIP port while production traffic stays on chan_sip.
What to read next
- PJSIP Configuration from Scratch for the full PJSIP object model
- Configuring PJSIP to Work Through NAT for NAT traversal settings
- res_pjsip module reference for all endpoint and transport options
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.