Master ring strategies to optimize call routing, reduce agent idle time, and improve contact center performance in ViciDial and Asterisk environments
Introduction
Ring strategy determines how Asterisk distributes calls across available agents in a queue. Choose the wrong strategy and you'll watch call times spike, agents go idle despite having calls waiting, and customers hear excessive hold times. Choose correctly and you maximize agent utilization while maintaining quality.
This guide walks you through every ring strategy Asterisk supports, explains when to use each one, and shows you exactly how to configure and monitor them in production ViciDial environments. You'll learn which strategies work best for predictive dialing, inbound call centers, and hybrid operations—and how to troubleshoot when ring strategy causes problems.
Prerequisites
Before implementing ring strategies in your Asterisk/ViciDial environment, confirm you have:
- Asterisk 11.0+ (14.x or 16.x recommended for ViciDial)
- ViciDial 2.12+ installed and fully operational
- Direct SSH/CLI access to your Asterisk server
- MySQL root or vicidial user access to the asterisk database
- Understanding of basic queue concepts: agents, queue members, extension status
- Current queue configuration in
/etc/asterisk/extensions-vicidial.conf - Ability to reload Asterisk (
asterisk -rx "core reload") without disrupting production - Log monitoring capability via
/var/log/asterisk/messages
If you're unfamiliar with ViciDial queue setup, run asterisk -rx "queue show" to verify your existing configuration is loading.
Understanding Asterisk Ring Strategies
Ring strategy is set in the queue.conf file and controls member selection when a call arrives in queue. Asterisk evaluates the strategy, picks the next agent, and sends the call to that extension.
Ring Strategy Types
Asterisk supports six core strategies:
- linear — Cycles through members in order (first-in-queue order)
- round-robin — Distributes calls evenly, tracking position
- round-robin queue member — Tracks per-queue position (most common in ViciDial)
- random — Selects random available agent
- fewest-calls — Routes to agent with fewest calls handled
- longest-idle — Routes to agent idle longest (agent reuse prevention)
ViciDial defaults to round-robin queue member for good reason: it balances load without requiring call counts and prevents agent burnout from rapid consecutive calls.
Why Ring Strategy Matters in ViciDial
In predictive dialers, agents answer calls immediately—ring strategy doesn't impact speed much. But in inbound queues and blended operations, ring strategy determines:
- Agent utilization — Proper strategy keeps agents working, not waiting
- Call distribution fairness — Prevents some agents from taking all calls
- Customer experience — Bad strategy causes calls to queue while agents are idle
- Compliance logging — ViciDial tracks agent activity; wrong strategy creates false idle time
Linear Ring Strategy
Linear sends each new call to the next agent in the queue member list, cycling from top to bottom. Once it reaches the last member, it restarts at the top.
When to Use Linear
- Small teams (3-5 agents) where agent positions are fixed
- Skill-based routing where agents are listed by specialization order
- Legacy systems where you need predictable agent assignment
- Testing and development environments
When NOT to Use Linear
- Medium-to-large teams (you'll create uneven distribution)
- Dynamic agent populations (agents logging in/out constantly)
- Predictive dialer environments (agents not present in time for calls)
Linear Configuration
[general]
persistmembers = yes
[inbound_queue]
type = queue
strategy = linear
members = SIP/2001,SIP/2002,SIP/2003
timeout = 15
retry = 5
maxlen = 0
announce-frequency = 60
announce-holdtime = yes
Testing Linear in ViciDial
Verify the queue is loaded and check member order:
asterisk -rx "queue show inbound_queue"
Expected output:
Queue: inbound_queue
Members:
SIP/2001 (dynamic) (In Call)
SIP/2002 (dynamic) (Not in use) (Penalty: 0)
SIP/2003 (dynamic) (Not in use) (Penalty: 0)
Strategy: linear
Calls in queue: 2
The next call goes to SIP/2002 (first available after SIP/2001).
Linear in ViciDial Database
Check queue configuration in the vicidial database:
SELECT * FROM vicidial_queues WHERE queue_name = 'inbound_queue'\G
If you don't see a queue_strategy field, your ViciDial version stores strategy only in extensions-vicidial.conf.
Round-Robin Ring Strategy
Round-robin selects agents in order, but remembers position between calls. Call 1 goes to agent 2, call 2 goes to agent 3, call 3 goes to agent 1—cycling continuously.
When to Use Round-Robin
- Medium teams (5-15 agents) with similar skill levels
- When you want to prevent agent burnout from repeated calls
- Blended inbound/outbound operations
- Phone system testing and demos
When NOT to Use Round-Robin
- Predictive dialers (round-robin doesn't align with agent readiness)
- Large centers (performance degrades with 50+ members)
- Skill-based routing
- ViciDial inbound queues (round-robin queue member is superior)
Round-Robin Configuration
[general]
persistmembers = yes
autofill = yes
autofillweight = 0
[sales_queue]
type = queue
strategy = round-robin
members = SIP/3001,SIP/3002,SIP/3003,SIP/3004
timeout = 20
retry = 5
maxlen = 0
announce-frequency = 60
announce-holdtime = yes
joinempty = yes
leavewhenempty = yes
Round-Robin in ViciDial
Create a test queue and monitor call distribution:
# Reload config
asterisk -rx "core reload"
# Check position tracking
asterisk -rx "queue show sales_queue"
# Make test calls and observe position change
After 4 calls, you'll see position reset to the beginning.
Round-Robin Queue Member Strategy (RRQM)
This is ViciDial's default and recommended strategy. It's identical to round-robin but resets position per queue, not globally. This is critical when agents belong to multiple queues.
Why RRQM for ViciDial
In a production contact center:
- Agents log into multiple queue types (inbound, tech support, sales)
- Round-robin global position breaks queue separation
- RRQM ensures fair distribution within each queue independently
When to Use Round-Robin Queue Member
- ViciDial inbound queues (always use this)
- Multi-queue environments
- Blended operations with queue specialization
- Any production ViciDial system with 3+ agents
RRQM Configuration
[general]
persistmembers = yes
autofill = yes
autofillweight = 0
[inbound_sales]
type = queue
strategy = rrmemory
members = SIP/4001,SIP/4002,SIP/4003,SIP/4004,SIP/4005
timeout = 25
retry = 5
maxlen = 0
announce-frequency = 60
announce-holdtime = yes
joinempty = yes
leavewhenempty = yes
weight = 100
[inbound_support]
type = queue
strategy = rrmemory
members = SIP/4001,SIP/4002,SIP/4006,SIP/4007
timeout = 30
retry = 5
maxlen = 0
announce-frequency = 60
announce-holdtime = yes
joinempty = yes
leavewhenempty = yes
weight = 80
Here, SIP/4001 and SIP/4002 are in both queues. RRQM maintains separate position tracking for each queue.
Verifying RRQM in Production
Check the active strategy:
asterisk -rx "queue show inbound_sales" | grep Strategy
Output:
Strategy: round robin with memory
Check agent distribution over 1 hour:
SELECT
user_extension,
COUNT(*) as call_count,
AVG(call_duration) as avg_duration
FROM vicidial_closer_log
WHERE call_date >= DATE_SUB(NOW(), INTERVAL 1 HOUR)
AND queue_name = 'inbound_sales'
GROUP BY user_extension
ORDER BY call_count DESC;
Equal call counts across agents confirms RRQM is working.
Random Ring Strategy
Random selects any available agent at random. No position tracking, no fairness guarantee per-call.
When to Use Random
- Very small queues (2-3 agents) as a quick test
- Kiosk/callback systems where agent identity doesn't matter
- Load testing Asterisk queue logic
- Emergency overflow scenarios
When NOT to Use Random
- Production ViciDial centers (ever)
- Any queue with more than 3 agents
- Compliance-heavy environments (call distribution is unpredictable)
- Agent scheduling (fairness reports become unreliable)
Random Configuration
[testing_queue]
type = queue
strategy = random
members = SIP/5001,SIP/5002,SIP/5003
timeout = 15
retry = 5
maxlen = 0
Why Not to Use Random in ViciDial
Run a call distribution test:
# Generate 100 test calls to random queue
for i in {1..100}; do
asterisk -rx "queue add member SIP/test$i to testing_queue"
done
Check distribution:
SELECT user_extension, COUNT(*) FROM vicidial_closer_log
WHERE queue_name = 'testing_queue'
GROUP BY user_extension;
Result: Expect uneven distribution. One agent may get 40+ calls while another gets 10.
Fewest-Calls Ring Strategy
Routes calls to the agent who has handled the fewest calls (all-time or in a configurable interval).
When to Use Fewest-Calls
- Fair load balancing across long shifts
- Ensuring low-performing agents aren't left idle
- Blended inbound/outbound where outbound dialing skews call counts
- Predictive dialers with manual agent selection
When NOT to Use Fewest-Calls
- High-volume predictive dialing (adds database overhead)
- ViciDial standard inbound (RRQM is lighter and sufficient)
- Real-time performance is critical (call selection latency)
- Agents with variable skill levels (you want experienced agents taking more calls)
Fewest-Calls Configuration
[blended_queue]
type = queue
strategy = fewest-calls
members = SIP/6001,SIP/6002,SIP/6003,SIP/6004,SIP/6005
timeout = 20
retry = 5
maxlen = 0
announce-frequency = 60
announce-holdtime = yes
joinempty = yes
leavewhenempty = yes
With fewest-calls, if call counts are:
- SIP/6001: 45 calls
- SIP/6002: 47 calls
- SIP/6003: 42 calls (fewest)
- SIP/6004: 49 calls
- SIP/6005: 46 calls
The next call routes to SIP/6003.
Monitoring Fewest-Calls Distribution
Check call counts in real time:
asterisk -rx "queue show blended_queue"
Log calls per agent per hour to verify algorithm:
SELECT
user_extension,
DATE_FORMAT(call_date, '%Y-%m-%d %H:00:00') as hour,
COUNT(*) as calls
FROM vicidial_closer_log
WHERE queue_name = 'blended_queue'
AND call_date >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
GROUP BY user_extension, hour
ORDER BY hour DESC, calls DESC;
Longest-Idle Ring Strategy
Routes calls to the agent idle the longest. Critical for preventing agent burnout and managing call pacing.
When to Use Longest-Idle
- Predictive dialers (prevents rapid-fire call pacing)
- Agent health and retention focus
- Compliance environments where call pacing matters
- Blended operations where you're mixing outbound prep time with inbound
When NOT to Use Longest-Idle
- Pure inbound call centers (unnecessary overhead)
- ViciDial with auto-dialing (dialer already manages pacing)
- Very small teams (performance difference is negligible)
Longest-Idle Configuration
[predictive_queue]
type = queue
strategy = longest-idle
members = SIP/7001,SIP/7002,SIP/7003,SIP/7004,SIP/7005,SIP/7006
timeout = 30
retry = 5
maxlen = 0
announce-frequency = 120
announce-holdtime = yes
joinempty = yes
leavewhenempty = yes
wrapuptime = 30
The wrapuptime = 30 parameter tells Asterisk an agent needs 30 seconds between calls. Longest-idle respects this.
Verifying Idle Time in ViciDial
Check agent idle tracking:
SELECT
user_extension,
user_name,
status,
last_call_time
FROM vicidial_users
WHERE active = 'Y'
ORDER BY last_call_time DESC;
Monitor idle period in real time:
watch -n 2 'asterisk -rx "queue show predictive_queue" | grep -A 50 "Members:"'
Penalty-Based Member Configuration
All strategies can incorporate member penalties. A penalty increases an agent's effective call count or idle time, effectively deprioritizing them.
Why Use Penalties
- Tier agents by skill (high-skill agents get lower penalties)
- Manage part-time vs. full-time distribution
- Temporarily reduce load on specific agents
- Handle agent unavailability gracefully
Penalty Syntax
[skilled_queue]
type = queue
strategy = rrmemory
members = SIP/8001,SIP/8002(penalty=5),SIP/8003(penalty=10),SIP/8004
timeout = 25
retry = 5
maxlen = 0
SIP/8001 (penalty=0, default): Highest priority SIP/8002 (penalty=5): Medium priority SIP/8003 (penalty=10): Lowest priority (used only if others unavailable) SIP/8004 (penalty=0): Equal priority to 8001
Dynamic Penalty in ViciDial
Adjust penalties without reloading config:
asterisk -rx "queue remove member SIP/8003 from skilled_queue"
asterisk -rx "queue add member SIP/8003(penalty=15) to skilled_queue"
Verify:
asterisk -rx "queue show skilled_queue" | grep -A 30 "Members:"
Configuring Queue Strategy in ViciDial
Via extensions-vicidial.conf
The authoritative configuration location for production ViciDial:
[vicidial_queues]
exten => 300,1,Answer()
exten => 300,n,Queue(inbound_queue,t)
exten => 300,n,Hangup()
[general]
persistmembers = yes
autofill = yes
[inbound_queue]
type = queue
strategy = rrmemory
members = SIP/2001,SIP/2002,SIP/2003,SIP/2004
timeout = 25
retry = 5
maxlen = 0
announce-frequency = 60
announce-holdtime = yes
joinempty = yes
leavewhenempty = no
Applying Configuration Changes
Reload without dropping calls:
asterisk -rx "core reload"
Verify queue loaded correctly:
asterisk -rx "queue show inbound_queue"
ViciDial Web Admin Configuration
Navigate to /vicidial/admin.php → System Settings → Queues (if available in your version).
Most modern ViciDial installations manage queues via extensions-vicidial.conf, but older versions (2.10 and earlier) may have GUI queue management. Always verify the loaded config:
grep -n "strategy" /etc/asterisk/extensions-vicidial.conf | head -20
Performance Considerations
CPU and Memory Impact
Different strategies have different overhead:
| Strategy | CPU Cost | Memory Cost | Best For |
|---|---|---|---|
| linear | Very Low | Minimal | Small teams |
| rrmemory | Low | Minimal | Production ViciDial |
| random | Very Low | Minimal | Testing |
| fewest-calls | Medium | Medium | Large centers |
| longest-idle | Medium | Medium | Predictive dialers |
Scaling Considerations
For large contact centers:
- Up to 20 agents: RRQM or linear work fine
- 20-50 agents: Use RRQM with penalties for skill tiers
- 50+ agents: Consider fewest-calls or longest-idle; monitor CPU
- 100+ agents: Use load balancing across multiple Asterisk boxes
Monitoring Queue Performance
Create a monitoring script to track strategy effectiveness:
#!/bin/bash
# /usr/local/bin/monitor_queues.sh
LOGFILE="/var/log/queue_monitor.log"
while true; do
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "=== $TIMESTAMP ===" >> $LOGFILE
asterisk -rx "queue show" >> $LOGFILE 2>&1
echo "" >> $LOGFILE
sleep 300 # Check every 5 minutes
done
Run in background:
nohup /usr/local/bin/monitor_queues.sh > /dev/null 2>&1 &
Ring Strategy Selection Matrix
Use this matrix to pick the right strategy for your scenario:
SCENARIO RECOMMENDED STRATEGY BACKUP
────────────────────────────────────────────────────────────────────
Small inbound (< 5 agents) linear rrmemory
Medium inbound (5-20 agents) rrmemory rrmemory
Large inbound (20+ agents) rrmemory + penalties fewest-calls
Predictive dialer longest-idle rrmemory
Blended (inbound + outbound) rrmemory or fewest longest-idle
Skill-based routing linear + penalties rrmemory
Multi-queue agents rrmemory (mandatory) N/A
Testing/Demo random or linear N/A
High compliance requirements longest-idle rrmemory
Troubleshooting Ring Strategy Issues
Symptom: Uneven Call Distribution
Cause: Wrong strategy or penalty misconfiguration
Diagnosis:
# Check queue strategy
asterisk -rx "queue show yourqueue" | grep Strategy
# Check member penalties
asterisk -rx "queue show yourqueue" | grep -A 50 "Members:"
Solution:
If using linear and distribution is uneven, switch to rrmemory:
[yourqueue]
strategy = rrmemory
Reload and verify:
asterisk -rx "core reload"
asterisk -rx "queue show yourqueue"
Symptom: Agents Sitting Idle While Calls Queue
Cause: Strategy doesn't match agent availability state, or agent status is stuck
Diagnosis:
# Check actual extension status
asterisk -rx "core show hints | grep SIP/2001"
# Check if agent is truly available
asterisk -rx "sip show peers | grep 2001"
# Check queue log for routing decisions
tail -50 /var/log/asterisk/queue_log
Solution:
Verify agent is actually available in Asterisk state machine:
asterisk -rx "device state list | grep SIP/2001"
If stuck NOT_INUSE when agent is on call, restart agent phone or kickout stale session:
asterisk -rx "sip notify check-sync SIP/2001"
Symptom: Strategy Changed But Old Behavior Persists
Cause: Config reload didn't take effect, or persistmembers saved old config
Diagnosis:
# Verify which config is loaded
asterisk -rx "module show like queue"
# Check persistent members file
ls -la /var/spool/asterisk/outgoing/
cat /var/spool/asterisk/outgoing/queue_members.conf 2>/dev/null
Solution:
Clear persistent member cache:
asterisk -rx "queue reset stats"
If that doesn't work, rebuild completely:
# Backup current config
cp /etc/asterisk/extensions-vicidial.conf /etc/asterisk/extensions-vicidial.conf.backup
# Remove persistent files
rm -f /var/spool/asterisk/outgoing/queue_members.conf
# Full restart Asterisk (careful in production)
systemctl restart asterisk
# Verify new strategy
asterisk -rx "queue show yourqueue" | grep Strategy
Symptom: High CPU Usage After Strategy Change
Cause: fewest-calls or longest-idle causing database queries per call
Diagnosis:
# Check CPU
top -b -n 1 | grep asterisk
# Check Asterisk process details
ps aux | grep asterisk | grep -v grep
# Monitor Asterisk queue processing
asterisk -rx "core show profile" | grep Queue
Solution:
If CPU spike correlates with fewest-calls strategy, downgrade to rrmemory:
# Change from
strategy = fewest-calls
# To
strategy = rrmemory
Reload and monitor:
asterisk -rx "core reload"
watch -n 1 'top -b -n 1 | grep asterisk'
Symptom: Random Strategy Causing Call Distribution Complaints
Cause: Random legitimately causes uneven distribution; agents perceive unfairness
Diagnosis:
Run hourly distribution report:
SELECT
user_extension,
COUNT(*) as total_calls,
ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM vicidial_closer_log WHERE queue_name = 'yourqueue' AND call_date >= DATE_SUB(NOW(), INTERVAL 1 HOUR)), 2) as percent
FROM vicidial_closer_log
WHERE queue_name = 'yourqueue'
AND call_date >= DATE_SUB(NOW(), INTERVAL 1 HOUR)
GROUP BY user_extension
ORDER BY total_calls DESC;
Solution:
Switch to rrmemory:
strategy = rrmemory
Re-run report after 1 hour—distribution will be nearly identical across agents.
Advanced: Custom Queue Logic via AGI
For edge cases where built-in strategies don't fit, use Asterisk Gateway Interface (AGI) to implement custom routing:
<?php
// /var/lib/asterisk/agi-bin/custom_route.php
$agi = new AGI();
$queue = $agi->request['agi_arg_1'];
// Fetch agents ordered by custom logic
$mysqli = new mysqli('localhost', 'root', 'password', 'asterisk');
$result = $mysqli->query("
SELECT user_extension
FROM vicidial_users
WHERE active = 'Y'
AND user_group = '$queue'
ORDER BY calls_today ASC
LIMIT 1
");
$row = $result->fetch_assoc();
$target = $row['user_extension'];
// Route call to chosen agent
$agi->exec('Dial', "SIP/$target");
?>
Call from extensions-vicidial.conf:
exten => 300,1,AGI(custom_route.php,inbound_queue)
exten => 300,n,Queue(inbound_queue,t)
This is overkill for 99% of installations but useful for complex skill-based routing.
Summary
Ring strategy is the invisible backbone of queue performance. Choose wisely:
For production ViciDial: Use
rrmemory(round-robin queue member) as your default. It's battle-tested, fair, and lightweight.For predictive dialers: Use
longest-idleto prevent agent burnout and control call pacing.For skill-based routing: Start with
linearorrrmemoryplus penalties to prioritize experienced agents.For large centers (50+ agents): Monitor
fewest-callsas an alternative, but verify CPU impact first.Never use
randomin production—it's called "random" for a reason.
Always verify configuration loaded correctly with asterisk -rx "queue show yourqueue", monitor distribution with database queries, and adjust penalties if specific agents need deprioritization.
The right ring strategy keeps your agents happy, your customers served quickly, and your contact center running efficiently. Get this right, and you'll never think about it again.