{"id":687,"date":"2026-05-04T16:32:30","date_gmt":"2026-05-05T00:32:30","guid":{"rendered":"https:\/\/nramkumar.org\/tech\/?p=687"},"modified":"2026-05-04T16:49:59","modified_gmt":"2026-05-05T00:49:59","slug":"asterisk-with-voip-ms-sip-trunk-in-debian-12","status":"publish","type":"post","link":"https:\/\/nramkumar.org\/tech\/blog\/2026\/05\/04\/asterisk-with-voip-ms-sip-trunk-in-debian-12\/","title":{"rendered":"Asterisk With voip.ms SIP trunk in Debian 12"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">One of my long-standing projects has been to have Asterisk running on my home network so my services have PSTN (and potentially SMS) access. In this post, I will cover the setup of Asterisk as a home lan pbx with voip.ms setup as the SIP trunk.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">VOIP Providers<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">While there are many VOIP providers, I like <a href=\"https:\/\/voip.ms\/\">voip.ms<\/a>. Their service is not the easiest to configure, setup and use for a simple home phone service. However, their service is reliable, well built once you understand the concepts and cost effective.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why Asterisk and not FreePBX<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">FreePBX is a good option if you want a managed UX for a complex PBX. However, I wanted to understand the basics and also with the help of AI build my own custom UX for my PBX down the line. For me, the goal is not getting a basic phone service &#8211; I already have a Grandstream ATA and home phones that handle that part with my voip.ms account. My goal is eventually have programmatic access to PSTN and I would like to use Asterisk REST Interface (ARI) and Asterisk Management Interface (AMI) to enable these.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Network Diagram<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s a network diagram of the final setup I wanted to achieve<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"559\" src=\"https:\/\/nramkumar.org\/tech\/wp-content\/uploads\/2026\/05\/AsteriskNetworkDiagram-1024x559.png\" alt=\"\" class=\"wp-image-690\" srcset=\"https:\/\/nramkumar.org\/tech\/wp-content\/uploads\/2026\/05\/AsteriskNetworkDiagram-1024x559.png 1024w, https:\/\/nramkumar.org\/tech\/wp-content\/uploads\/2026\/05\/AsteriskNetworkDiagram-300x164.png 300w, https:\/\/nramkumar.org\/tech\/wp-content\/uploads\/2026\/05\/AsteriskNetworkDiagram-768x419.png 768w, https:\/\/nramkumar.org\/tech\/wp-content\/uploads\/2026\/05\/AsteriskNetworkDiagram.png 1408w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Setup Environment<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I chose to run Asterisk as a LXC on my proxmox server. I decided to place it in my IoT VLAN. Asterisk is a fairly large attack surface, so placing it in my IoT VLAN and isolated from my other devices works for me. My management VLAN has full access to this VLAN &#8211; so any other services that need access to ARI or AMI will be able to reach it. I set the LXC up with a static IP on my Unifi gateway.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Installation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of building packages from source, I decided to use Sangoma&#8217;s packages on Debian 12. As of FreePBX 17, Sangoma now uses Debian as its underlying Operating System so this works well.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First we create the user and group for asterisk, add Sangoma repo and install Asterisk and key dependencies:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Create asterisk user before installing\naddgroup asterisk\nadduser --system --ingroup asterisk asterisk\n\n# Add Sangoma repo\napt-get update\napt-get -y install gnupg software-properties-common wget\nwget http:\/\/deb.freepbx.org\/gpg\/aptly-pubkey.asc\ngpg --dearmor --yes -o \/etc\/apt\/trusted.gpg.d\/freepbx.gpg aptly-pubkey.asc\nadd-apt-repository -y -S \"deb &#91; arch=amd64 ] http:\/\/deb.freepbx.org\/freepbx17-prod bookworm main\"\napt-get update\n\n# Install Asterisk and required packages\napt-get -y install asterisk22 asterisk22-core asterisk22-configs asterisk22-odbc\n\n# Install key dependencies\napt-get install -y libxslt1.1 liburiparser1 libsrtp2-1\n\n# Install some sound files\napt-get install -y asterisk-sounds-core-en-ulaw asterisk-sounds-extra-en-ulaw\n\n# Install troubleshooting tools\napt-get install -y bind9-dnsutils nmap tcpdump\n\n# Add a home directory for asterisk user (optional)\nusermod -d \/home\/asterisk asterisk\n\n# Change ownership of directories to asterisk user\/group\nchown -R asterisk:asterisk \/etc\/asterisk \/var\/{lib,log,spool}\/asterisk \/usr\/lib\/asterisk\n\n# Modify config to run asterisk as user\/group asterisk\nsed -i 's\/;runuser\/runuser\/' \/etc\/asterisk\/asterisk.conf\nsed -i 's\/;rungroup\/rungroup\/' \/etc\/asterisk\/asterisk.conf\n\n# Enable and run the service\nsystemctl enable asterisk\nsystemctl start asterisk<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">At this point, you should have a working asterisk service that does not do much. You can check using <code>systemctl status asterisk.service<\/code> or <code>ps aux | grep asterisk | grep -v grep<\/code> to see the status of the service and process details. Use the system logs to debug issues if the service does not run for any reason. Also, after this point, you should be able to run further commands as user <code>asterisk<\/code> instead of running as <code>root<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Enable ARI and HTTP<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ARI requires the HTTP server in Asterisk, which is disabled by default. Edit <code>\/etc\/asterisk\/http.conf<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>enabled=yes\nbindaddr=0.0.0.0\nbindport=8088<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then configure ARI credentials in <code>\/etc\/asterisk\/ari.conf<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;general]\nenabled=yes\npretty=yes\n\n&#91;asterisk]\ntype=user\nread_only=no\npassword=YOUR_ARI_PASSWORD<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Restart the service (<code>systemctl restart asterisk.service<\/code>) and verify modules are loaded with <code>asterisk -rx \"module show like res_ari\"<\/code><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting things up at voip.ms<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">voip.ms uses subaccounts to allow you to register from different endpoints for the same number (DID). Each subaccount gets its own SIP credentials. This allows my Grandstream ATA to connect directly to voip.ms (no dependency on my Asterisk server) and other softphones to also connect directly. For Asterisk, I created a dedicated subaccount with the following settings (only the relevant ones are mentioned):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Authentication Type: User\/Password Authentication<\/li>\n\n\n\n<li>Device Type: Asterisk, IP PBX, Gateway or VoIP Switch<\/li>\n\n\n\n<li>CallerID Number: Use one of my DIDs (pick your DID)<\/li>\n\n\n\n<li>NAT (Network Address Translation): Yes<\/li>\n\n\n\n<li>Encrypted SIP Traffic: Yes<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">For incoming calls, you can create a ring group and set it as the target for your DID &#8211; then add the sub-accounts to that ring group. This allows the flexibility (and continued use of your DID) while you setup Asterisk by not having that sub-account in the ring group. Go to DID Numbers &gt; Ring Groups and Create new ring group. Add relevant sub-accounts to the ring group along with the setting for time, number of rings. Now DID Numbers &gt; Manage DID and Routing Settings, select Ring Group and pick the Ring Group you created.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting Up voip.ms SIP Trunk<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Edit <code>\/etc\/asterisk\/pjsip.conf<\/code> to add the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;transport-tls]\ntype=transport\nprotocol=tls\nbind=0.0.0.0:5061\nca_list_file=\/etc\/ssl\/certs\/ca-certificates.crt\nmethod=tlsv1_2\nlocal_net=&lt;lan-subnet&gt;\/24\n\n&#91;voipms-auth]\ntype=auth\nauth_type=userpass\nusername=&lt;sub-account userid&gt;\npassword=&lt;sub-account password&gt;\n\n&#91;voipms-aor]\ntype=aor\ncontact=sip:&lt;pop hostname&gt;:5061;transport=tls\n\n&#91;voipms]\ntype=endpoint\ntransport=transport-tls\ncontext=from-voipms\ndisallow=all\nallow=ulaw\noutbound_auth=voipms-auth\naors=voipms-aor\nfrom_user=&lt;sub-account userid&gt;\nfrom_domain=&lt;pop hostname&gt;\ndirect_media=no\nrtp_symmetric=yes\nforce_rport=yes\nrewrite_contact=yes\nmedia_encryption=sdes\nsend_rpid=yes\ntrust_id_outbound=yes\n\n&#91;voipms-registration]\ntype=registration\ntransport=transport-tls\noutbound_auth=voipms-auth\nserver_uri=sip:&lt;pop hostname&gt;:5061;transport=tls\nclient_uri=sip:&lt;sub-account userid&gt;@&lt;pop hostname&gt;\nretry_interval=60\n\n&#91;voipms-identify]\ntype=identify\nendpoint=voipms\nmatch=&lt;pop hostname&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>pop hostname<\/code> is the pop server you picked for your DID in voip.ms (check DID &gt; Manage DID &gt; Edit Settings &gt; DID Point of Presence). Please note that much of the settings under <code>[voipms]<\/code> have been setup to work correctly behind a NAT with your Asterisk server running on a LAN address behind your router. In particular, <code>rtp_symmetric, direct_media, force_rport, rewrite_contact<\/code> settings are relevant here. Also, these settings setup an encrypted SIP connection as well as enable encrypted calls via <code>SRTP<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Restart the service (<code>systemctl restart asterisk.service<\/code>) and check that registration is successful with <code>asterisk -rx \"pjsip show registrato<\/code>ns&#8221;. If everything worked, you should see <code>voipms<\/code> show as <code>Registered<\/code>. At this point, you are ready to test basic outbound calling using this SIP trunk.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Testing basic outbound calling<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Edit <code>\/etc\/asterisk\/extensions.conf<\/code> and add the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;general]\nstatic=yes\nwriteprotect=no\n\n&#91;default]\nexten =&gt; s,1,Answer()\n same =&gt; n,Playback(demo-congrats)\n same =&gt; n,Hangup()\n\n&#91;from-voipms]\nexten =&gt; s,1,Answer()\n same =&gt; n,Playback(demo-congrats)\n same =&gt; n,Hangup()\n\n&#91;outbound]\nexten =&gt; _1NXXNXXXXXX,1,NoOp(Outbound call to ${EXTEN})\n same =&gt; n,Dial(PJSIP\/${EXTEN}@voipms)\n same =&gt; n,Hangup()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now restart the service and test an outbound call to another number (like  your cell):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>systemctl restart asterisk.service\nasterisk -rx \"channel originate PJSIP\/1XXXXXXXXXX@voipms application Playback demo-congrats\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If everything worked, you should get a call on your cell and when answered you should hear something like &#8220;Congratulations, you have successfully installed and executed asterisk&#8221;.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting Up Local SIP Endpoints<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Now that we have the basic SIP trunk with voip.ms working with Asterisk, we can start setting up local SIP endpoints &#8211; these would be how endpoints connect to the Asterisk server and we can wire them up to receive\/make calls via the SIP trunk we setup. I setup my local endpoints in a separate file <code>\/etc\/asterisk\/pjsip_extensions.conf<\/code> and then <code>#include pjsip_extensions.conf<\/code> in <code>\/etc\/asterisk\/pjsip.conf<\/code>. Here&#8217;s what the extension configuration looks like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>; The Local Endpoint\n&#91;100]\ntype=endpoint\ncontext=from-internal\ndisallow=all\nallow=ulaw\nauth=100-auth\naors=100\nrtp_symmetric=yes\nrewrite_contact=yes\nforce_rport=yes\ndirect_media=no\n\n; The Authentication Credentials\n&#91;100-auth]\ntype=auth\nauth_type=userpass\npassword=&lt;password for asterisk user&gt;\nusername=&lt;username for asterisk user&gt; ; I just used 100\n\n; The Address of Record\n&#91;100]\ntype=aor\nmax_contacts=5\nremove_existing=yes\nminimum_expiration=60\ndefault_expiration=120<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now, you can use this information configure local softphone clients. Since I use a Linux Desktop, I tried three different clients &#8211; they all are good. A brief description of my experience with each below:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/www.linphone.org\/en\/download\/\">Linphone<\/a> &#8211; Configuration took a bit of effort. The UX isn&#8217;t great. Getting audio working took some work as it enabled SRTP by default which I don&#8217;t support in my local Asterisk server.<\/li>\n\n\n\n<li><a href=\"https:\/\/www.zoiper.com\/\">Zoiper<\/a> &#8211; The easiest to setup. Pretty much everything worked once the basic account setup was done.<\/li>\n\n\n\n<li><a href=\"https:\/\/jami.net\/\">Jami<\/a> &#8211; Configuring a SIP account is not obvious from the UI, but you can <a href=\"https:\/\/docs.jami.net\/en_US\/user\/sip-account.html#configuring-an-account-on-jami\">read the documentation to figure it out.<\/a> Once setup, also uses SRTP by default and needs adjustment if that&#8217;s not supported. Do not install as a flatpak or snap &#8211; this leads to frustrating issues with permissions. I just installed the Debian\/Ubuntu package and it worked fine.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Have a client configured and verify that you can sign-in using the SIP account you setup on your Asterisk server.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Configuring Outbound And Inbound Calls From Local SIP Endpoints<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Next, we can configure the outbound and inbound calls through the SIP trunk from our Local SIP endpoints. Modify your <code>\/etc\/asterisk\/extensions.conf<\/code> to add the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;default]\nexten =&gt; s,1,Answer()\n same =&gt; n,Playback(demo-congrats)\n same =&gt; n,Hangup()\n\n&#91;from-voipms]\nexten =&gt; &lt;Your DID&gt;,1,NoOp(Inbound call - Name: ${CALLERID(name)} - Num: ${CALLERID(num)})\n same =&gt; n,Set(CALLERID(name)=${IF($&#91;\"${TOLOWER(${CALLERID(name)})}\" = \"wireless caller\" | \"${CALLERID(name)}\" = \"unknown\" | \"${CALLERID(name)}\" = \"\"]?${CALLERID(num)}:${CALLERID(name)})})\n same =&gt; n,Dial(${PJSIP_DIAL_CONTACTS(100)},30)\n same =&gt; n,Hangup()\n\n; Catch-all safety\nexten =&gt; _X.,1,Dial(PJSIP\/100,30)\n same =&gt; n,Hangup()\n\n&#91;outbound]\nexten =&gt; _1NXXNXXXXXX,1,NoOp(Outbound call to ${EXTEN})\n same =&gt; n,Set(CALLERID(num)=&lt;Your DID&gt;)\n same =&gt; n,Set(CALLERID(name)=&lt;Your Name&gt;)\n same =&gt; n,Dial(PJSIP\/${EXTEN}@voipms)\n same =&gt; n,Hangup()\n\n&#91;from-internal]\n; Match 10-digit numbers (e.g., 5555551234)\nexten =&gt; _NXXNXXXXXX,1,NoOp(Outbound call to ${EXTEN})\n same =&gt; n,Set(CALLERID(num)=&lt;Your DID&gt;)\n same =&gt; n,Dial(PJSIP\/${EXTEN}@voipms)\n same =&gt; n,Hangup()\n\n; Match 11-digit numbers (e.g., 15555551234)\nexten =&gt; _1NXXNXXXXXX,1,NoOp(Outbound call to ${EXTEN})\n same =&gt; n,Set(CALLERID(num)=&lt;Your DID&gt;)\n same =&gt; n,Dial(PJSIP\/${EXTEN}@voipms)\n same =&gt; n,Hangup()\n\n; Allow dialing extension 100 locally (for future use)\nexten =&gt; 100,1,Dial(${PJSIP_DIAL_CONTACTS(100)},30)\n same =&gt; n,Hangup()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">With this dialplan, your local SIP endpoints should now be able to both make outbound calls through the voip.ms SIP trunk as well as receive inbound calls made to your voip.ms number.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Debugging Tools<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If something is not working, run the following and repeat the scenario to get logs and then figure out what went wrong. AI can be helpful in figuring out the problem:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>asterisk -rvvv\npjsip set logger on<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of my long-standing projects has been to have Asterisk running on my home network so my services have PSTN (and potentially SMS) access. In this post, I will cover the setup of Asterisk as a home lan pbx with voip.ms setup as the SIP trunk. VOIP Providers While there are many VOIP providers, I&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-687","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/posts\/687","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/comments?post=687"}],"version-history":[{"count":3,"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/posts\/687\/revisions"}],"predecessor-version":[{"id":692,"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/posts\/687\/revisions\/692"}],"wp:attachment":[{"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/media?parent=687"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/categories?post=687"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/tags?post=687"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}