VMware ESXi and vSphere Cluster Management
Asterisk Variable Types: Global, Shared, Channel, and Environment Variables
Learn how Asterisk global, shared, channel, and environment variables differ, including syntax, lifetime, inheritance, SHARED(), ENV(), Dial(), and troubleshooting.
A dialplan is Asterisk’s call-routing logic, commonly written in extensions.conf. Variables hold values that Asterisk can use while it evaluates dialplan applications and functions. Examples include a caller’s account tag, a selected destination, a retry counter, or an endpoint string passed to Dial().
Variable substitution means replacing a variable reference with its current value. The usual syntax is ${VARIABLE_NAME}:
same => n,NoOp(The selected route is ${ROUTE_TARGET})
A variable has both a value and a scope. The value is the stored text, such as PJSIP/300. The scope describes which channels can access it and how long it remains available. Choosing the correct scope prevents calls from seeing one another’s private data, allows related call legs to coordinate when necessary, and provides a suitable place for system-wide configuration.
Variable scope overview
Asterisk dialplans commonly use four variable types:
| Variable type | How it is defined or accessed | Visibility | Lifetime | Best use cases | Key risks or limitations |
|---|---|---|---|---|---|
| Global | [globals] or Set(GLOBAL(name)=value) | Available across channels in the Asterisk process | Configuration-defined values are loaded with the dialplan; runtime values are in process memory unless stored elsewhere | Common endpoints, dialing destinations, and non-call-specific settings | All calls can observe or overwrite mutable values; unsuitable for per-call state |
| Shared | SHARED(name,channel) | Accessible through the specified variable name and explicitly targeted channel identifier | Runtime coordination data; clean up deliberately when it is no longer needed | Communication between related call legs | Concurrency, stale values, incorrect channel identifiers, and key collisions |
| Channel | Set(NAME=value) | Normally limited to one channel | Usually ends when that channel is destroyed; selected values can be inherited by child channels | Caller data, route choices, counters, and temporary call state | Not automatically visible on another call leg |
| Environment | ENV(NAME) | Values supplied to the Asterisk process by the operating system or service manager | Normally follows the process environment and deployment lifecycle | Host- or deployment-specific configuration | Not call state; process environment data may expose sensitive information |
Syntax reference
| Task | Recommended syntax | Notes |
|---|---|---|
| Define a startup global | [globals] | Loaded when the dialplan configuration is loaded or reloaded. |
| Set a channel variable | Set(ROUTE_TARGET=SIP/301) | Creates or changes a variable on the current channel. |
| Reference a normal variable | ${ROUTE_TARGET} | Substitution occurs when the application argument is evaluated. |
| Read or write a shared value | ${SHARED(note,channel-name)}Set(SHARED(note,channel-name)=text) | The channel identifier must identify the channel whose shared value is being addressed. |
| Read an environment value | ${ENV(SITE_NAME)} | Reads a value from the Asterisk process environment. |
| Set an inherited channel variable | Set(_NAME=value) or Set(__NAME=value) | A single underscore passes one generation; a double underscore passes through subsequent child generations. |
Global variables
A global variable is visible to all channels in the Asterisk process after it is defined. Startup globals are normally placed in the [globals] section of extensions.conf:
[globals]
HELLEN=SIP/300
COMMON_SUPPORT=PJSIP/support
[example]
exten => 300,1,Dial(${HELLEN})
When extension 300 runs, ${HELLEN} is expanded to SIP/300, so that value becomes the target argument for Dial(). The same idea works with PJSIP endpoint names:
[globals]
SALES_ENDPOINT=PJSIP/sales
[example]
exten => 300,1,NoOp(Calling ${SALES_ENDPOINT})
same => n,Dial(${SALES_ENDPOINT})
Globals are appropriate for centrally maintained endpoint strings, common destinations, feature settings, and other values that do not describe one particular call. They are unsafe for mutable per-call state. If two simultaneous calls use the same global as a retry counter or caller identifier, one call can overwrite the other call’s value.
Values written in [globals] are configuration data loaded when Asterisk loads or reloads the dialplan. A value changed at runtime with Set(GLOBAL(NAME)=value) is process state. Do not assume that a runtime change survives a restart. Reload behavior for runtime globals can vary by Asterisk version and implementation details, so represent intended permanent configuration in loaded configuration or use a durable external store for persistent business data.
[example]
exten => 310,1,Set(GLOBAL(MAINTENANCE_TARGET)=PJSIP/maintenance)
same => n,NoOp(Current target: ${MAINTENANCE_TARGET})
Modern dialplans normally use Set(GLOBAL(name)=value). Older dialplans may contain SetGlobalVar; treat that as a legacy, version-dependent mechanism and verify behavior against the Asterisk version in use.
Channel variables
A channel variable belongs to one Asterisk channel. It is the normal choice for information associated with the current call path:
[example]
exten => 301,1,Set(ROUTE_TARGET=SIP/301)
same => n,Set(RETRY_COUNT=0)
same => n,NoOp(Route ${ROUTE_TARGET}, retry ${RETRY_COUNT})
same => n,Dial(${ROUTE_TARGET})
Ordinary channel variables normally disappear when their channel is destroyed. This makes them useful for temporary caller data, selected destinations, authentication results, retry counters, and routing decisions. Older dialplans may use SetVar, but current dialplans normally use Set().
Channel inheritance
Channel inheritance is the propagation of selected channel variables to a channel created from the current channel. A leading underscore controls inheritance:
NAME: ordinary local variable; not automatically inherited._NAME: inherited by the next child channel, normally for one generation.__NAME: inherited by child channels and remains marked for further generations.
[example]
exten => 304,1,Set(__ACCOUNT_TAG=gold)
same => n,Set(_RETRY_LIMIT=2)
same => n,Dial(PJSIP/304)
The double-underscore value is intended to remain available through later child-channel creation. The single-underscore value is intended for the next generation. Inheritance must be set before the child channel is created; setting a variable after Dial() has created the called channel is too late for that child.
If a value is needed only by the current channel, use an ordinary channel variable. If a directly spawned child needs it, use inheritance. If separately identified related channels must exchange data, use SHARED() or a suitable external storage mechanism.
Shared variables with SHARED()
A shared variable is accessed through the SHARED() dialplan function. It is intended for passing or coordinating data between related channels. A shared value is not simply a normal channel variable copied everywhere: access is determined by the shared variable name and the explicitly targeted channel identifier.
That distinction matters. A lookup such as SHARED(call_note,channel-name) addresses the call_note value associated with the channel named channel-name. The reading channel must know which channel it intends to address.
Worked caller-to-called-channel example
This example explicitly records the originating channel name, writes a shared note against that channel, inherits the identifier into the called channel, and retrieves the note there:
[example]
exten => 303,1,Set(ORIGINATING_CHANNEL=${CHANNEL(name)})
same => n,Set(__ORIGINATING_CHANNEL=${ORIGINATING_CHANNEL})
same => n,Set(SHARED(call_note,${ORIGINATING_CHANNEL})=priority-caller)
same => n,NoOp(Shared note set for ${ORIGINATING_CHANNEL})
same => n,Dial(Local/303-called@internal)
[internal]
exten => 303-called,1,NoOp(Originating channel: ${ORIGINATING_CHANNEL})
same => n,NoOp(Note from caller: ${SHARED(call_note,${ORIGINATING_CHANNEL})})
same => n,Hangup()
Here, ${CHANNEL(name)} establishes the exact originating channel identifier before the shared value is written. The double-underscore variable carries that identifier to the child channel. The called channel then uses the inherited identifier as the second SHARED() argument. A production dialplan should also consider whether the channel name remains valid for the entire coordination period and should clear or replace shared data when processing ends.
Shared values need careful concurrency design. Use unique channel identifiers or keys where simultaneous calls could otherwise collide. Do not leave stale values available for later calls, and avoid placing private caller information in a shared location unless every channel that can access it is trusted and the lifetime is controlled.
Environment variables
An environment variable originates outside the dialplan, in the environment supplied to the Asterisk process by the operating system, container runtime, or service manager. Read it with ENV():
[example]
exten => 302,1,Set(SITE_NAME=${ENV(SITE_NAME)})
same => n,NoOp(Service site: ${SITE_NAME})
This example reads the host-provided SITE_NAME value and copies it into a channel variable. Reading an OS environment value is different from setting a dialplan variable: ENV(SITE_NAME) obtains process-environment data, while Set(SITE_NAME=value) assigns a channel variable unless a special variable namespace is used.
For a systemd-managed service, configure the value in the service or an environment file supplied to that service, not only in the shell of an administrator who manually runs commands. Restart or otherwise reload the service according to the service configuration after changing its environment. Treat process environment data as potentially exposed to diagnostics, process inspection, or service-management tooling. Do not use environment variables for per-call state.
Variable evaluation in applications
Expansion occurs when Asterisk evaluates an application’s arguments. A stored endpoint is therefore separate from the final dial target:
[globals]
OUTBOUND_ENDPOINT=PJSIP/301
[example]
exten => 320,1,NoOp(Target before expansion: ${OUTBOUND_ENDPOINT})
same => n,Dial(${OUTBOUND_ENDPOINT})
At execution time, ${OUTBOUND_ENDPOINT} becomes PJSIP/301, and Dial() receives that expanded string. This lets one value be reused, but it does not remove the need to use the correct channel technology and endpoint format.
Pay attention to punctuation. Commas often separate application arguments, spaces can affect parsing, and channel technology strings can contain syntax meaningful to the application. If a value contains commas, spaces, or application-specific delimiters, use the quoting and escaping rules appropriate to that application and test the exact expanded argument. Keep complex values in a form that does not collide with the application’s argument separators when possible.
During testing, inspect values immediately before they are consumed:
same => n,NoOp(ROUTE_TARGET=${ROUTE_TARGET})
same => n,Verbose(1,Account tag is ${ACCOUNT_TAG})
same => n,Dial(${ROUTE_TARGET})
Do not log passwords, tokens, or private caller data. The Asterisk CLI can also help validate the loaded dialplan and active channels:
asterisk -rx "dialplan show globals"
asterisk -rx "dialplan show 300@example"
asterisk -rx "core show channels"
asterisk -rx "core show channel <channel-name>"
Choosing the correct variable type
| Requirement | Recommended variable type | Reason |
|---|---|---|
| Value is only needed during one call | Channel | It is isolated to the current channel and normally disappears with it. |
| Value must be available to a spawned call leg | Inherited channel variable | _NAME or __NAME deliberately propagates selected data. |
| Related channels must exchange a value | Shared | SHARED() addresses data associated with an explicitly selected channel. |
| Value is common across the PBX | Global | All channels in the process can use a centrally defined configuration value. |
| Value comes from the operating-system service environment | Environment | ENV() reads deployment-specific process configuration. |
Use globals for configuration constants, not mutable call records. Use shared variables for deliberate cross-channel coordination, not as an unstructured global scratchpad. The more widely visible a value is, the greater the privacy and concurrency impact of mistakes.
Troubleshooting variable problems
Dial() receives an empty or unexpected destination
- Confirm the variable was assigned before expansion.
- Check spelling and capitalization.
- Verify that the global section was loaded or reloaded.
- Confirm the channel technology and endpoint format.
- Add
NoOp()orVerbose()immediately beforeDial(). - Confirm the active context, extension, and priority with the Asterisk CLI.
A value is missing on the destination channel
- An ordinary channel variable is not automatically inherited.
- The variable may have been set after the child channel was created.
- Use
_NAMEor__NAMEaccording to the intended inheritance lifetime. - If the channels need explicit cross-channel communication, use
SHARED()and verify the target channel identifier.
Simultaneous calls overwrite one another’s values
- Replace global per-call state with a channel variable where possible.
- Make shared keys or targeted channel identifiers unique to the call.
- Generate concurrent test calls to expose collisions and timing errors.
ENV() returns an empty value
- Check whether the variable is present in the environment of the Asterisk service process.
- Confirm it was configured for the service rather than only for an interactive shell.
- Restart the service if its environment was changed and the service manager requires a restart.
- Verify the exact environment variable name and log the result with
NoOp()during a test call.
A runtime global disappears after restart
A runtime global existed in process memory but was not necessarily persistent configuration. Decide whether the value is static configuration, transient call state, or persistent business data. Put static values in [globals] and use a durable external store for data that must survive restarts.
Exam-relevant notes
${NAME}performs variable substitution; it does not by itself determine the variable’s scope.Set(NAME=value)normally sets a channel variable, whileSet(GLOBAL(NAME)=value)changes a global.[globals]defines startup or reload-time global configuration._NAMEprovides one-generation channel inheritance;__NAMEprovides multi-generation inheritance.SHARED()is for explicitly addressed cross-channel data, not ordinary local channel-variable access.ENV()reads the Asterisk process environment and should not be used for per-call state.- Normal channel variables are usually lost when their channel ends.
For a compact reference while working in a dialplan, see Asterisk variable types.