Modular Dialplan Organization

Configuration Asterisk 18+ -- Last reviewed 2026-03-29 dialplan configuration include organization best-practices Found this useful? Upvote it. ×

Modular Dialplan Organization

As a dialplan grows beyond a few dozen extensions, maintaining everything in a single extensions.conf becomes unmanageable. The #include directive lets you split the dialplan across multiple focused files.

File Structure

/etc/asterisk/
  extensions.conf           <- Master file, includes everything
  extensions_main.conf      <- Include hub
  extensions_phones.conf    <- User group contexts
  extensions_services.conf  <- Utility/service extensions
  extensions_cos.conf       <- Class of Service route contexts

Master File (extensions.conf)

[general]
static = yes
writeprotect = no
clearglobalvars = yes

[globals]
TRUNK = PJSIP/trunk-endpoint
RINGTIME = 20

#include extensions_main.conf

Include Hub (extensions_main.conf)

; This file included by extensions.conf
; It includes all the sub-files in the right order

#include extensions_phones.conf
#include extensions_services.conf
#include extensions_cos.conf

Service Extensions (extensions_services.conf)

[services]

; Speaking clock
exten => *86,1,Answer()
 same => n,Playback(silence/1)
 same => n,SayUnixTime()
 same => n,Hangup()

; Echo test
exten => *87,1,Answer()
 same => n,Echo()
 same => n,Hangup()

; Record and playback test
exten => *88,1,Answer()
 same => n,Playback(silence/1)
 same => n,Playback(beep)
 same => n,Record(test_${CALLERID(num)}:wav)
 same => n,Wait(1)
 same => n,Playback(test_${CALLERID(num)})
 same => n,Hangup()

Phone Contexts (extensions_phones.conf)

[office]
include => services
include => internal
include => pstn-local
include => pstn-long-distance
include => catchall

[lobby]
include => services
include => internal
include => catchall

Route Contexts (extensions_cos.conf)

[internal]
exten => _1XXX,1,Dial(PJSIP/${EXTEN},${RINGTIME})

[pstn-local]
exten => _9NXXXXXX,1,Dial(${TRUNK}/${EXTEN:1})

[pstn-long-distance]
exten => _91NXXNXXXXXX,1,Dial(${TRUNK}/${EXTEN:1})

[catchall]
exten => _X.,1,Playback(an-error-has-occurred)
 same => n,Congestion()

How it works

  1. #include filename: A preprocessor directive that inserts the contents of the named file at that point. It runs at parse time, so the result is as if everything were in one file. Paths are relative to /etc/asterisk/.
  2. Include order matters: Files are parsed top-to-bottom. If a context is referenced by include => before the file defining it is #include'd, the dialplan will still work because include => is resolved at runtime, not parse time. However, keeping #include order logical aids readability.
  3. #include vs include =>: These are completely different. #include is a file-level preprocessor directive that pulls in another config file. include => is a dialplan directive that makes one context search another context for extension matches.
  4. [globals] section: Define shared variables (trunk name, ring timeout, etc.) once in extensions.conf. All contexts across all included files can reference ${TRUNK} and ${RINGTIME}.
  5. static = yes: Prevents the dialplan from being modified at runtime via AMI or pbx_config. This is the safest setting for file-based dialplans.

Tips

User Notes

No notes yet. Be the first to contribute a tip or example.

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.

Moderated before publishing. Email never shown.

Related Snippets