SSD Nodes Learn 🎉 VPS from $5.50/mo
How to do am Matt ConnorBy Matt Connor

How to Run Asterisk or FreePBX for Your Own VoIP

Run Asterisk or FreePBX on your VPS with SIP trunks, RTP UDP port ranges, firewall rules to block scanners, and tips to stop toll fraud.

Wetin be a self-hosted VoIP server

A self-hosted VoIP server na SIP server wey dey run for VPS wey you control. This mean say your own machine dey route your calls instead of hosted phone service. VoIP (voice over IP) dey carry speech as UDP packets. SIP (session initiation protocol) na the signalling wey dey set up and end call. Audio no dey pass through SIP. Na this one fact dey cause most of the wahala wey dey below.

Four parts dey make the system work.

  • The PBX (private branch exchange) software. Asterisk na the usual choice. E dey keep the extensions and the dialplan.
  • The endpoints. Desk phones or softphones wey dey register with the PBX using username and secret.
  • The SIP trunk. Na paid account with provider wey connect you to public telephone network and rent real phone numbers give you.
  • The media path. RTP (real-time transport protocol) flows dey carry the audio through their own UDP ports.

To self-host the PBX no mean say you dey self-host phone numbers. Carrier dey provide the numbers, and you still dey pay per number and per minute. Wetin you own na the call routing, voicemail, recordings, and extension list. You also own the security of service wey people dey attack because of money.

Which ports self-hosted VoIP server need?

SIP signalling dey use port 5060 for UDP and TCP, and port 5061 for SIP over TLS (transport layer security). Na only call setup dey pass through those ports. The audio for each call na separate UDP flow, wey dey go to one port wey e take from RTP range. Asterisk dey ship with sample rtp.conf wey dey set rtpstart=10000 and rtpend=20000, and compiled-in defaults na 5000 and 31000. Each call dey use two ports from that range: one for RTP and one for RTCP (RTP control protocol).

Na this separation dey make most first attempts fail. Call go connect, both phones go show say dem answer am, but nobody go hear anything because firewall allow 5060 and drop every RTP packet. Signalling and media na separate flows, so dem need separate firewall rules. If this distinction new to you, how ports and listening sockets dey work for Linux fit help you read am before you open anything.

Make the range smaller before you open am. Twenty thousand ports plenty pass wetin small system need. Since each call dey use two ports, range of two hundred fit handle one hundred calls at once.

[general]
rtpstart=10000
rtpend=10200

Apply am with sudo asterisk -rx "core reload".

Asterisk ba FreePBX: which one you suppose install?

Asterisk na the engine. You dey configure am with text files for /etc/asterisk, and na you go write the dialplan yourself. FreePBX na web interface wey dem write with PHP and JavaScript. E dey sit on top Asterisk, generate those files for you, and add modules for voicemail and call queues.

The important difference for VPS na who get control of the machine. As of August 2026, the official FreePBX 17 installer expect vanilla Debian 12 system. E go install Asterisk, web server, database server, and PHP. If you point am to machine wey already dey run other services, the result no go good. Give FreePBX e own VPS.

wget https://github.com/FreePBX/sng_freepbx_debian_install/raw/master/sng_freepbx_debian_install.sh -O /tmp/sng_freepbx_debian_install.sh
sudo bash /tmp/sng_freepbx_debian_install.sh

The install log dey for /var/log/pbx/freepbx17-install.log. Na the first place you suppose check when the script stop early.

FreePBX dey control the configuration files wey e generate. If you edit pjsip.conf by hand for FreePBX machine, your change go disappear next time the GUI write that file. FreePBX dey read separate files wey get _custom for the name for hand-written config, and e no dey touch those ones.

The honest trade-off be this. FreePBX give you GUI, and e put login page for your phone system on public internet. Raw Asterisk no get web surface at all. Every setting na documented directive wey you fit read inside file and keep for git. If you install FreePBX, restrict the web port to your own address or access am through VPN, because admin GUI for PBX na target wey get direct route to money.

Version note, correct as of August 2026: Asterisk 22 na the current long-term support release. Dem publish am for October 2024, and e go receive security fixes until October 2028. Asterisk 23 na the standard release. Ubuntu 24.04 get Asterisk 20.6.0 for the universe repository.

Install Asterisk for Ubuntu 24.04

Distribution package na the quick way. Ubuntu patch am, and e go start under systemd by itself.

sudo apt update
sudo apt install -y asterisk
sudo asterisk -rx "core show version"

If you build am from source, you go get the current long term support release instead.

sudo apt update
sudo apt install -y build-essential wget
cd /usr/local/src
sudo wget https://downloads.asterisk.org/pub/telephony/asterisk/asterisk-22-current.tar.gz
sudo tar -xzf asterisk-22-current.tar.gz
cd asterisk-22.*
sudo contrib/scripts/install_prereq install
sudo ./configure
sudo make menuselect
sudo make -j"$(nproc)"
sudo make install
sudo make samples
sudo make config
sudo ldconfig

install_prereq install go bring the build dependencies for your distribution, while install_prereq test go print the commands wey e for run without changing anything. make menuselect go open the module picker, where you fit enable codec_opus under Codec Translators. Run make samples only for fresh install, because e go write the sample configuration inside /etc/asterisk. make config go install the init script for /etc/init.d/asterisk, and systemd go drive am through e SysV compatibility layer, so sudo systemctl enable --now asterisk go work afterwards.

No matter which way you use, sudo asterisk -rvvv go connect to the daemon wey dey run and give you the CLI. If you run core show version there, e go show wetin you actually install.

Configure SIP trunk and one extension

PJSIP na the SIP channel driver for current Asterisk. E configuration dey inside /etc/asterisk/pjsip.conf, and small typed sections wey reference each other by name build am. Sections wey get different types fit still share one name, na why every block below dey use mytrunk.

[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0

[mytrunk]
type=registration
outbound_auth=mytrunk
server_uri=sip:sip.example.com
client_uri=sip:1234567890@sip.example.com
retry_interval=60

[mytrunk]
type=auth
auth_type=userpass
username=1234567890
password=REPLACE_WITH_A_LONG_RANDOM_SECRET

[mytrunk]
type=aor
contact=sip:sip.example.com:5060

[mytrunk]
type=endpoint
context=from-trunk
disallow=all
allow=ulaw
outbound_auth=mytrunk
aors=mytrunk

[mytrunk]
type=identify
endpoint=mytrunk
match=sip.example.com

The registration object dey send REGISTER wey tell your provider where to deliver your calls. The identify object na how Asterisk match call wey enter from provider to this endpoint by source address, and provider usually publish several addresses wey you go list there. Outbound registration and endpoint na separate objects by design: one tell provider where you dey, the other decide wetin go happen to the calls.

Desk phone need three more objects.

[6001]
type=endpoint
context=internal
disallow=all
allow=ulaw
auth=auth6001
aors=6001
direct_media=no

[auth6001]
type=auth
auth_type=userpass
username=6001
password=REPLACE_WITH_A_LONG_RANDOM_SECRET

[6001]
type=aor
max_contacts=1

direct_media=no keep Asterisk for the media path. If you leave am out, Asterisk go try make the two endpoints send RTP directly to each other. This go fail when phone dey behind NAT (network address translation) for home router. Never make the secret match the extension number. The upstream sample use password=6001 for extension 6001 so the example go easy to read, and scanners dey try exactly that pattern first.

The dialplan inside /etc/asterisk/extensions.conf decide wetin each context fit do.

[internal]
exten => 6001,1,Dial(PJSIP/6001,20)
exten => _9X.,1,Dial(PJSIP/${EXTEN:1}@mytrunk,60)

[from-trunk]
exten => 1234567890,1,Dial(PJSIP/6001,20)
same => n,Hangup()

Those two contexts na security boundary. from-trunk handle calls wey enter from provider and fit only ring extension 6001. E no fit reach the _9X. pattern, so call from outside no fit dial out again through your trunk. If you merge the contexts, you don build the classic toll fraud path: stranger call your number, your dialplan dial out with your account, and you pay for am.

Apply the configuration and check am.

sudo asterisk -rx "pjsip reload"
sudo asterisk -rx "pjsip show registrations"
sudo asterisk -rx "pjsip show endpoints"

pjsip show registrations suppose list mytrunk with status Registered. Rejected mean say provider reject your credentials. Unregistered mean say your REGISTER no get response, so check the firewall next.

Firewall rules for SIP and RTP

Signalling and media no be the same thing because the risk wey dem carry different. Restrict 5060 to the addresses wey your provider really dey use and the networks wey your phones dey on.

sudo ufw allow proto udp from 203.0.113.10 to any port 5060
sudo ufw allow proto tcp from 203.0.113.10 to any port 5060
sudo ufw allow 10000:10200/udp
sudo ufw status verbose

You no fit narrow the RTP range reach the same level, because media often dey come from different address from the signalling. Ask your provider for their media subnets, and restrict to dem if dem publish am. Keep the range only as wide as your busiest hour need. Default policies, rule order and the rest of the ufw model dey covered for the ufw firewall basics guide for a VPS.

Check IPv6 too. If IPV6=no dey set for /etc/default/ufw, ufw no dey filter IPv6 at all. This mean say daemon wey bind to :: fit reachable through IPv6 without any of the rules above applying. Opening ports for IPv6 with ufw explain how the two families differ. Most providers also give you separate network firewall for the control panel. That firewall dey enforce before your packets reach the VPS, so the port must open for both places.

SIP brute force and toll fraud no be optional wahala

Once you put 5060 for public address, scans go start. The pattern steady: REGISTER and INVITE requests from plenty source addresses, dem dey try common extension numbers with common secrets. Asterisk dey log every failure, and the line get this shape.

Request 'REGISTER' from '<sip:1000@198.51.100.20>' failed for '198.51.100.20:5060' (callid: 5f1a5c0d) - No matching endpoint found

The reason this one need serious effort na money. Person fit steal extension use am make expensive international calls, often to premium rate numbers wey dey pay attacker share. Na you go pay the bill because the calls use your credentials. E dey run at machine speed, and most times e dey run overnight.

Six controls dey mandatory, no be optional hardening.

  • Never create extension wey its secret na the same number or short word. Generate one with openssl rand -base64 24 and paste am inside.
  • Leave anonymous inbound calls off. PJSIP dey refuse unidentified calls by default, and e only accepts dem if you create endpoint named anonymous. No create am.
  • Keep trunk context separate from any context wey fit dial out, as e show above.
  • Restrict signalling by source address, both for ufw and for your provider network firewall.
  • Set spending cap with your SIP trunk provider and disable international destinations wey you never call. Na this only control fit limit the loss when the other controls fail.
  • Run fail2ban against the Asterisk log.

Asterisk fit also raise security event after repeated unmatched requests from one address. The [global] section of pjsip.conf takes unidentified_request_count, wey default to 5, and unidentified_request_period, wey default to 5 seconds. Together, this mean five unmatched requests from one address inside five seconds go produce security event wey fail2ban fit act on.

Ban the scanners with fail2ban

fail2ban don ship with one asterisk jail wey dem don write already. E cover ports 5060 and 5061, e dey read /var/log/asterisk/messages, and default na maxretry = 10. Jail go remain off until you enable am for /etc/fail2ban/jail.local.

[asterisk]
enabled = true
maxretry = 5
findtime = 600
bantime = 86400
sudo systemctl restart fail2ban
sudo fail2ban-client status asterisk

Healthy result go list the jail log file and count of addresses wey dey banned now. For public 5060, that count go stop being zero within one day. The shipped filter dey match the No matching endpoint found notices wey dey above, and e still dey match Asterisk structured SecurityEvent lines. Those events dey enter separate log channel wey dem comment out for /etc/asterisk/logger.conf, so enable am there and add the file to the jail logpath if you want make dem dey match.

[logfiles]
console => notice,warning,error
messages.log => notice,warning,error
security.log => security

Reload the logger with sudo asterisk -rx "logger reload". The filter still get one journalmatch for asterisk.service, so journal backend go work if you prefer make log files no dey. Installation, jail.local structure and how to unban address wey lock you out dey covered for fail2ban guide for Ubuntu 24.04.

Delay and codec choice for far VPS

Geography set delay, and you no fit configure am away. ITU-T G.114 recommend make one-way delay stay below 150 ms for normal conversation, and e regard up to about 400 ms as still usable. Audio from phone dey go your VPS, then go your trunk provider, so VPS wey dey wrong region go carry that journey two times. Put am near the phones or near the provider. If the two options no align, prefer location near the phones, because that leg usually dey pass through consumer internet where jitter dey worst.

Codec choice set bandwidth for each call. Every codec for here dey send packet every 20 ms. That na 50 packets per second. Each packet carry 40 bytes of IP, UDP, and RTP headers on top the audio payload.

ChartBandwidth per concurrent call, one direction, 20 ms packets
The data behind this chart
[
  {
    "label": "G.711 ulaw",
    "payload_kbps": 64,
    "ip_kbps": 80
  },
  {
    "label": "G.722",
    "payload_kbps": 64,
    "ip_kbps": 80
  },
  {
    "label": "Opus at 24 kbps",
    "payload_kbps": 24,
    "ip_kbps": 40
  },
  {
    "label": "G.729",
    "payload_kbps": 8,
    "ip_kbps": 24
  }
]

G.711 ulaw na the default for most trunks. E payload na 64 kbps. With headers, active call dey use 80 kbps for each direction. Opus for 24 kbps dey use 40 kbps, while G.729 reduce am to 24 kbps, but audio quality and CPU time go suffer. These figures na header arithmetic, no be measurements: payload rate plus 40 bytes per packet at 50 packets per second. Ethernet or VLAN framing add small extra amount for the wire.

Transcoding dey use CPU. If your phones and trunk both speak ulaw, allow only ulaw, and Asterisk go pass the audio through without change. Opus dey perform well for links wey packet loss dey affect, but transcoding between Opus and G.711 need external codec_opus module. You select am for make menuselect, and e no dey built by default.

Wetin dey break, and the string wey you go see

The call connect but nobody hear anything. RTP no dey arrive. Confirm say the range for rtp.conf na the same range wey you open for firewall, then use sudo tcpdump -ni any udp portrange 10000-10200 monitor packets while you dey make call. If no packet dey show at all, your firewall or your provider network firewall dey drop dem.

Audio dey only one direction. One side dey send RTP go address wey no fit receive am. This na address problem, no be port problem. If your VPS get public address directly for the interface, you no need NAT handling. If provider give the VPS private address with one-to-one public mapping, set the public address for the transport and list your private range for local_net.

[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0
local_net=10.0.0.0/8
external_media_address=198.51.100.5
external_signaling_address=198.51.100.5

No matching endpoint found for the log. The request no match any endpoint by IP address or username. If scanner generate am, na normal, and fail2ban go handle am. If na your own provider generate am, e mean say the identify section no list the address wey dem dey use call you from.

The registration status na Rejected. The provider reject the credentials for your auth section. Run pjsip set logger on for the CLI, monitor one REGISTER and the response wey follow am, then compare client_uri and username with wetin provider issue you.

Nothing useful dey show for the log. By default, Asterisk dey write notice level and above to messages.log. Increase am with core set verbose 4 and pjsip set logger on while you reproduce the problem, then turn both off, because the SIP logger dey write every packet.

Before you expose am

PBX no be like other things wey you self-host. If web app spoil, na one page you lose. If PBX spoil, e fit turn to phone bill within hours while you dey sleep. Run am for VPS wey no dey do any other work, restrict 5060 to addresses wey you know, give every extension random secret, and set spending limit for the trunk account. The rest of the server still need the same basic protection like any server wey internet fit reach, as this explanation of how safe VPS hosting really be show.

FAQ

Which ports I need open for self-hosted VoIP server?

Port 5060 for SIP signalling over UDP and TCP, port 5061 if you use SIP over TLS, and one range of UDP ports for RTP media. Asterisk sample rtp.conf uses 10000 to 20000, and the compiled-in defaults na 5000 to 31000. Each call dey use two ports from the range, so range of two hundred ports fit handle one hundred simultaneous calls. Open the RTP range as UDP, and restrict 5060 to your provider addresses and your own networks instead of leaving am open to everybody.

Make I install Asterisk by itself or use FreePBX?

Install raw Asterisk when you want small attack surface and configuration files wey you fit keep for git, and you dey ready write the dialplan. Install FreePBX when you want GUI for extensions, voicemail and call queues. As of August 2026, FreePBX 17 installer dey expect vanilla Debian 12 machine and e installs Asterisk, web server, database server and PHP, so give am VPS of its own. FreePBX dey regenerate the config files wey e manages, so put hand edits inside its _custom files.

Why audio no dey after the call connect?

The signalling work, but the media no work. SIP set up the call for port 5060, and the audio na separate UDP flow to one port for the RTP range wey something dey drop. Check say the range for rtp.conf match the range wey firewall open, and check your provider network firewall together with the one for the server. Run sudo tcpdump -ni any udp portrange 10000-10200 during call: if no packets show, dem block am before dem arrive.

How I go stop SIP brute force attacks and toll fraud?

Give every extension long random secret. Make e never match the extension number. Keep the context wey your trunk uses separate from any context wey fit dial out, so inbound call no fit dial back out with your account. Restrict port 5060 to your provider addresses. Enable the asterisk jail for fail2ban; e dey read /var/log/asterisk/messages and ban addresses wey dey produce No matching endpoint found failures. Then set spending cap and block international destinations wey you no use with your provider, because na only this control fit cap the loss if the other protections fail.

VPS for far region fit spoil call quality?

Yes, because audio dey pass through two legs: phone to VPS, then VPS to trunk provider. ITU-T G.114 recommends one-way delay under 150 ms, and VPS wey no dey well placed fit use most of that budget on distance alone. Choose region wey close to the phones, because that leg usually dey run over consumer internet where jitter dey worst. Codec choice no fit fix delay; e only changes bandwidth. So G.729 fit save bytes, but e no go rescue 200 ms path.

#voip#asterisk#freepbx#sip#self-hosting