You run a BPO. Three clients. Client A needs 30 agents on insurance leads. Client B needs 15 doing appointment setting. Client C needs 50 on a political survey. All three expect total isolation -- their leads are confidential, their recordings are privileged, their performance data is proprietary. Running three separate VICIdial installations would triple your server costs and management overhead.
VICIdial handles this. Not perfectly -- the multi-tenancy is administrative rather than database-level -- but well enough that thousands of BPOs worldwide run multiple clients on shared instances. The key is understanding what VICIdial can and can't isolate, and configuring it so Client A never sees Client B's data.
The Architecture You're Working With
VICIdial was not designed from the ground up as a multi-tenant platform. It was designed as a single-organization dialer that gained multi-tenancy through its permission system. This distinction matters because it defines the boundaries.
What Multi-Tenancy Means in VICIdial
Three pillars:
- User Groups -- Every user belongs to a group that controls which admin screens, reports, campaigns, and recordings they can access.
-
Campaign Assignment -- Campaigns are associated with user groups via
allowed_campaigns. Users only see campaigns their group permits. - Permission Granularity -- Dozens of fine-grained permissions covering admin screens, report types, and operational capabilities.
What Multi-Tenancy Does NOT Mean
-
No database-level isolation. All tenants share the same MySQL
asteriskdatabase. Client A's leads sit in the samevicidial_listtable as Client B's. Differentiated by list_id and campaign_id, not separate databases. - No Asterisk-level isolation. Shared SIP channels, conference bridges, recording resources.
- No OS-level isolation. No containers, no VMs, no process separation.
The isolation is enforced at the application layer through the admin interface. A user with direct MySQL or SSH access can see all tenants' data. For most BPOs this is fine -- clients see VICIdial through the admin UI and the reports you provide.
For clients with contractual requirements for database-level isolation (HIPAA, SOC 2, government), you need separate VICIdial instances.
User Group Configuration
User groups are the foundation. Every client gets their own groups, and permissions determine exactly what they see.
Creating Client Groups
| Group ID | Users | Admin Level | Purpose |
|---|---|---|---|
| CLIENT_A_ADMIN | Managers | 7 | Run reports, configure campaigns |
| CLIENT_A_AGENT | Agents | 1 | Log in, take calls, disposition |
| CLIENT_B_ADMIN | Managers | 7 | Same but restricted to Client B |
| CLIENT_B_AGENT | Agents | 1 | Same |
| BPO_ADMIN | Internal team | 9 | Full access to everything |
Why separate ADMIN and AGENT groups? Managers need report access. Agents need to log in and take calls. Different permission levels.
The Critical Setting: Allowed Campaigns
In the user group config, the Allowed Campaigns field lists which campaigns users in this group can see. This is the most important setting for tenant isolation.
Allowed Campaigns: CLIENT_A_SALES CLIENT_A_SURVEY CLIENT_A_INBOUND
Users only see these campaigns in admin, agent login, and reports. All other campaigns are invisible.
Important: Must be manually updated when you create or remove campaigns for a client. Forgetting to add a new campaign means the client can't see it. Forgetting to remove a reassigned campaign means data leakage.
Admin Screen Permissions
For a client manager group:
| Permission | Recommended | Notes |
|---|---|---|
| Admin Access | 7 or 8 | Never 9 for clients |
| Campaigns | View only | Or Modify for trusted clients |
| Lists | View and Modify | Clients upload their own leads |
| Users | View | Can see their agents |
| Carriers | NONE | Never expose trunk config |
| DIDs | View only | Can see their assigned numbers |
| Servers | NONE | No infrastructure visibility |
| System Settings | NONE | Never |
| User Groups | NONE | Never |
Recording Access
| Setting | Client Manager | Client Agent |
|---|---|---|
| Recording Access | ALL | OWN or NONE |
| Allowed Recording Campaigns | Their campaigns only | Their campaigns only |
Client managers listen to any recording from their campaigns but can't access other clients'. Agents hear only their own calls (for self-improvement) or nothing.
Campaign Isolation and Naming
Use consistent naming that includes the client identifier:
CLIENT_A_SALES_OB (Client A outbound sales)
CLIENT_A_INBOUND (Client A inbound)
CLIENT_B_APPT_SET (Client B appointment setting)
This makes administration clearer and per-client billing queries straightforward.
List Organization
Assign list ID ranges per client:
| Client | Range | Example |
|---|---|---|
| Client A | 1000-1999 | 1001 = Client A Fresh Insurance Leads |
| Client B | 2000-2999 | 2001 = Client B Appointment Leads March |
| Client C | 3000-3999 | 3001 = Client C Voter File Q1 |
| Internal/Test | 9000-9999 | 9001 = Internal Test List |
Not enforced by VICIdial -- it's organizational discipline. But it makes auditing visually obvious.
Data Leakage Vectors
Vector 1: Campaigns in wrong user group. Double-check Allowed Campaigns when adding or removing campaigns. Quarterly audit.
Vector 2: Lists assigned to wrong campaigns. Client A's list accidentally on Client B's campaign = Client B's agents dial Client A's leads. Audit list-to-campaign assignments monthly.
Vector 3: Direct database access. If a client has any MySQL access for custom reporting, they could query vicidial_list and see everything. Mitigate with MySQL views:
CREATE VIEW client_a_leads AS
SELECT * FROM vicidial_list WHERE list_id BETWEEN 1000 AND 1999;
GRANT SELECT ON asterisk.client_a_leads TO 'client_a_report'@'%';
Vector 4: Real-time report. Log in as a client user and verify they only see their own agents.
SIP Trunk Allocation
Three strategies:
Strategy 1: Shared Trunk Pool
All clients share trunks. Simplest. Cheapest. Highest utilization -- idle capacity from one client automatically available to another. But one client's spike can starve another, caller ID management is more complex, and a carrier outage hits everyone.
Strategy 2: Dedicated Per Client
Separate carrier entries per client. Complete isolation, clean cost attribution. Higher cost because each client pays for idle capacity. More configuration work.
Strategy 3: Hybrid
Dedicated primary trunk per client with a shared overflow pool. Guaranteed capacity for each client, overflow prevents busy signals during spikes. Most complex to configure.
Rule of thumb: Small clients (<10 agents) share trunks. Medium (10-50) get dedicated trunks -- enough volume to justify their own allocation, and they typically have caller ID and compliance requirements. Large (50+ agents) get dedicated trunks and possibly their own VICIdial instance.
Per-Tenant Billing
Most BPOs bill on agent hours, call minutes, or a combination. VICIdial provides the raw data for any model. Track agent hours by user group, outbound minutes by campaign, inbound minutes by inbound group, and recording storage by campaign.
Automate monthly billing reports with a script that runs on the 1st, querying vicidial_log and vicidial_agent_log filtered by campaign and date range. Map campaigns to clients and generate per-client summaries.
Capacity Planning for Multi-Tenant
Server Sizing
| Total Agents | Configuration |
|---|---|
| Under 25 | Single server, all roles |
| 25-75 | Web + DB server, separate telephony |
| 75-150 | Web, DB, 2-3 telephony servers |
| 150-300 | Full cluster with redundancy |
| 300+ | Consider separate instances |
Add 20-30% buffer over single-tenant sizing for multi-tenant overhead.
Concurrent Call Capacity
Each outbound agent with predictive dialing at 3:1 ratio generates 3 concurrent SIP channels plus inbound:
- Client A: 30 agents x 3 = 90 + 10 inbound = 100 channels
- Client B: 15 agents x 2 = 30 + 5 inbound = 35 channels
- Client C: 50 agents x 4 = 200 + 0 inbound = 200 channels
- Total: 335 concurrent channels
Each Asterisk server on modern hardware handles 250-350 concurrent channels with recording active. This three-client scenario needs at least 2 telephony servers.
Noisy Neighbor Problems
One client's traffic spike degrades others. Mitigations:
- CPU contention from AMD processing: Monitor per-campaign channel usage
- Hopper contention: Large lists fill the hopper slower and delay other campaigns. Stagger hopper levels.
- Recording I/O: 200 concurrent recordings write ~200 MB/minute. Use separate disks for recordings vs database.
Limitations to Be Honest About
No per-tenant resource guarantees. VICIdial has no resource quotas or QoS at the tenant level. First-come-first-served. Overprovision (run at 60-70% capacity) to mitigate.
Shared system settings. Recording format, session timeouts, API controls, timezone settings -- all global. You can't have Client A on WAV and Client B on MP3.
Admin level 9 sees everything. Operationally required but may concern clients wanting absolute isolation. Limit level 9 to a small number of trusted administrators.
When to Split
Signals you need separate instances:
- Client requires database-level isolation (regulatory or contractual)
- Client has 100+ agents impacting other tenants
- Client needs custom VICIdial modifications
- Client needs different Asterisk or VICIdial versions
- Client demands dedicated infrastructure contractually
The cost trade-off: shared instance for 3 clients = $200-500/month total. Dedicated instances = $200-500/month per client plus 3x administration. The hybrid approach most BPOs land on: large anchor clients get dedicated instances, medium clients share with 2-3 similar-sized clients, small clients share a multi-tenant instance with 5-10 others.
DID Routing Per Tenant
Most BPO clients have their own inbound phone numbers that route to specific campaigns and inbound groups.
For each client with inbound:
- Navigate to Admin > Inbound Groups and create a group: CLIENT_A_INBOUND
- Navigate to Admin > DIDs and create entries for the client's numbers, routing to their inbound group
- Set call times, queue priority, and after-hours routing per client
Per-Client IVR
Each client wants their own greeting and menu structure. Create separate call menus per client with custom audio, then route each option to the client-specific inbound group.
Track DID inventory by client for accurate billing. Which DIDs belong to which client, what's the monthly carrier cost per DID, and are any DIDs unused.
Example: Complete 3-Client BPO Configuration
Clients: Apex Insurance (25 OB + 5 IB agents), GreenSolar (15 OB agents), VoteReach (40 OB agents, seasonal)
User Groups
| Group | Users | Level | Allowed Campaigns |
|---|---|---|---|
| BPO_SUPER | 2 super admins | 9 | All |
| BPO_MANAGER | 4 managers | 8 | All |
| APEX_MANAGER | 2 | 7 | APEX_OB_SALES, APEX_INBOUND |
| APEX_AGENT | 30 | 1 | APEX_OB_SALES, APEX_INBOUND |
| GREEN_MANAGER | 1 | 7 | GREEN_APPTSET |
| GREEN_AGENT | 15 | 1 | GREEN_APPTSET |
| VOTE_MANAGER | 2 | 7 | VOTE_SURVEY_Q1 |
| VOTE_AGENT | 40 | 1 | VOTE_SURVEY_Q1 |
Campaigns
| Campaign | Client | Lists | Carrier |
|---|---|---|---|
| APEX_OB_SALES | Apex | 1001-1050 | APEX_SIP (dedicated) |
| APEX_INBOUND | Apex | N/A | APEX_SIP |
| GREEN_APPTSET | GreenSolar | 2001-2020 | SHARED_POOL |
| VOTE_SURVEY_Q1 | VoteReach | 3001-3100 | SHARED_POOL |
Infrastructure
| Server | Spec | Role |
|---|---|---|
| Server 1 | 8-core, 32 GB, 2 TB SSD | DB + Web |
| Server 2 | 8-core, 16 GB, 1 TB SSD | Telephony (Apex + Green) |
| Server 3 | 8-core, 16 GB, 1 TB SSD | Telephony (VoteReach, seasonal) |
Server 3 can be decommissioned when VoteReach's seasonal campaign ends, reducing costs during off-peak.
FAQ: The Questions Every BPO Asks
Can an agent work across multiple clients? Yes, but it breaks isolation. If an agent's group has access to both Client A and Client B campaigns, they could see data from both. Create a separate cross-client group and understand the trade-off.
Per-client DNC lists? Use per-campaign DNC (Admin > Campaigns > DNC settings). A number DNC for Client A isn't automatically DNC for Client B. The system-wide DNC applies globally -- use it for numbers that should be blocked for everyone.
Can clients manage their own campaigns? With level 7-8 and campaign modify permissions limited to their allowed campaigns, they can adjust dial levels, recycling, and dispositions. Start with read-only, upgrade as the client proves competent.
Per-client trunk limits? VICIdial doesn't have built-in per-campaign trunk limits. Limit the max dial level on a client's campaign to indirectly cap concurrent trunk usage. 20 agents x dial level 3.0 = max 60 outbound channels.
Branding the agent interface? Not natively -- same agent screen for all groups. Use campaign-level scripts to display client-specific logos and content in the script panel. For fully separate branded interfaces, build custom agent screens using VICIdial's agent API.
Client onboarding? Create user groups, campaigns, lists, carrier entries (if dedicated), DID routing. Assign list ID ranges. Create users. Test with a small pilot list.
Client offboarding? Export data (leads, CDR, recordings). Deactivate campaigns, users, groups -- don't delete (billing disputes, compliance). Archive recordings. Hold DIDs 30-90 days in case client returns.
ViciStack builds multi-tenant VICIdial deployments for BPOs with proper isolation, per-client reporting, and scalable infrastructure that grows with your client roster.
Originally published at https://vicistack.com/blog/vicidial-multi-tenant/
Top comments (0)