Knowledgebase › Asterisk without FreePBX — minimal pjsip.conf for a single SIP trunk

Asterisk without FreePBX — minimal pjsip.conf for a single SIP trunk

FreePBX® is excellent for managing complex deployments, but if you just need Asterisk to bridge one carrier to a handful of extensions — or if you want a clean reference install for learning — bare Asterisk is dramatically simpler. This article walks the minimal pjsip configuration for a single SIP trunk and two internal extensions.

When to skip FreePBX

  • You only need a few features (single trunk, a few extensions, basic routing).
  • You want config in plain text files that work with Git and Ansible.
  • You're building a custom application on top of Asterisk and FreePBX's database schema gets in the way.
  • You want to learn how SIP and Asterisk actually work.

When NOT to skip FreePBX: anything where end users need to manage their own settings, complex IVR, queue ACD, commercial modules (EPM, App Manager), or anyone who'd rather click than edit text files.

Install

On Debian 12:

apt update
apt install asterisk
systemctl enable --now asterisk

On AlmaLinux/Rocky 9:

dnf install asterisk
systemctl enable --now asterisk

The package install gives you Asterisk plus a default set of sample configs in /etc/asterisk/. We're going to replace the SIP-related ones.

The five files you need

  • /etc/asterisk/pjsip.conf — SIP endpoints, trunks, transports.
  • /etc/asterisk/extensions.conf — dialplan.
  • /etc/asterisk/rtp.conf — RTP port range.
  • /etc/asterisk/logger.conf — log channels (default usually fine).
  • /etc/asterisk/modules.conf — which modules to load (default usually fine).

pjsip.conf — transports, trunk, extensions

; ============================================================
; Transports
; ============================================================
[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0:5060
external_media_address=<your-pbx-public-ip>
external_signaling_address=<your-pbx-public-ip>

; ============================================================
; Trunk to your carrier (example: Telnyx)
; Replace credentials and host with your actual values.
; ============================================================
[trunk-telnyx]
type=registration
outbound_auth=trunk-telnyx-auth
server_uri=sip:sip.telnyx.com
client_uri=sip:<your-username>@sip.telnyx.com
contact_user=<your-username>
retry_interval=30
forbidden_retry_interval=600

[trunk-telnyx-auth]
type=auth
auth_type=userpass
username=<your-username>
password=<your-password>

[trunk-telnyx-aor]
type=aor
contact=sip:sip.telnyx.com

[trunk-telnyx]
type=endpoint
transport=transport-udp
context=from-trunk
disallow=all
allow=ulaw
allow=alaw
outbound_auth=trunk-telnyx-auth
aors=trunk-telnyx-aor
from_user=<your-username>
from_domain=sip.telnyx.com
direct_media=no

[trunk-telnyx]
type=identify
endpoint=trunk-telnyx
match=sip.telnyx.com

; ============================================================
; Internal extensions
; ============================================================
[1001]
type=endpoint
transport=transport-udp
context=from-internal
disallow=all
allow=ulaw
allow=alaw
auth=1001-auth
aors=1001
direct_media=no
rtp_symmetric=yes
force_rport=yes
rewrite_contact=yes

[1001-auth]
type=auth
auth_type=userpass
username=1001
password=<strong-password-for-extension>

[1001]
type=aor
max_contacts=2
qualify_frequency=30

; (Repeat for 1002, 1003, etc.)

extensions.conf — dialplan

[from-internal]
; Dial an internal extension
exten => _1XXX,1,NoOp(Internal call to ${EXTEN})
 same => n,Dial(PJSIP/${EXTEN},20)
 same => n,Voicemail(${EXTEN}@default,u)
 same => n,Hangup()

; Dial outbound — 10 or 11 digit US numbers
exten => _NXXNXXXXXX,1,NoOp(Outbound call to ${EXTEN})
 same => n,Dial(PJSIP/+1${EXTEN}@trunk-telnyx)
 same => n,Hangup()

exten => _1NXXNXXXXXX,1,NoOp(Outbound call to ${EXTEN})
 same => n,Dial(PJSIP/+${EXTEN}@trunk-telnyx)
 same => n,Hangup()

; Voicemail access
exten => *97,1,VoiceMailMain(${CALLERID(num)}@default)
 same => n,Hangup()

[from-trunk]
; Inbound DID — route to extension 1001
exten => <your-did-here>,1,NoOp(Inbound call from ${CALLERID(num)})
 same => n,Dial(PJSIP/1001,30)
 same => n,Voicemail(1001@default,u)
 same => n,Hangup()

rtp.conf

[general]
rtpstart=10000
rtpend=20000
rtpkeepalive=15

voicemail.conf (if you want voicemail)

[default]
1001 => 1234,Front Desk,frontdesk@example.com
1002 => 1234,Sales,sales@example.com

Reload and test

asterisk -rvvv
> core reload
> pjsip show registrations
> pjsip show endpoints

The trunk should show as Registered. Endpoints show Not in use (no phone registered yet).

Register a SIP phone with username 1001, password matching 1001-auth, server <your-pbx-ip>:5060. The endpoint flips to Available; you can call out.

Firewall

Open:

  • UDP 5060 — SIP signaling (restrict source to your carrier's IP block where possible).
  • UDP 10000-20000 — RTP audio.
  • If you add TLS or WSS later: TCP 5061 (SIP-TLS) and TCP 8089 (WSS).

From here

This is enough to make and receive calls. To extend:

  • Add more extensions by duplicating the 1001 block.
  • Add IVR with the Background() and WaitExten() dialplan apps.
  • Add queues with the app_queue module and queues.conf.
  • Add CDR with cdr.conf and cdr_csv (or cdr_mysql for database CDR).
  • Move to chan_pjsip realtime if you need DB-backed extension config.

If you find yourself reinventing IVR and queue management, that's a sign FreePBX would actually save you time — go back to it without shame.

Also Read

« « Back

Powered by WHMCompleteSolution