Deploying Your Own Workers
Step-by-step guide to deploying a CallMeter worker as a Docker container in your infrastructure, including configuration, verification, and multi-worker setups.
This guide walks you through deploying a CallMeter worker in your own infrastructure. By the end, you will have a Docker container running in your network, connected to CallMeter, and ready to execute SIP tests against your private infrastructure.
The entire process takes about 10 minutes.
Prerequisites
Before you begin, ensure you have:
- Docker installed on the host machine (Docker Engine 20.10+ or Docker Desktop)
- Outbound network access to
gw.callmeter.ioon port443(TCP) --- this is the Worker Gateway - Network access to your SIP infrastructure --- the worker must be able to reach your PBX, SBC, or SIP proxy on the SIP signaling port (typically 5060/UDP or 5061/TLS)
- A CallMeter account with an active worker license (Standard, Professional, or Enterprise)
- A project created in CallMeter where the worker will be assigned
Quick Prerequisite Check
Run these commands on the host machine to verify readiness before proceeding:
# 1. Verify Docker is installed and running
docker --version
# Expected: Docker version 20.10+ or higher
# 2. Verify outbound connectivity to the Worker Gateway
nc -zv gw.callmeter.io 443
# Expected: Connection to gw.callmeter.io port 443 [tcp/https] succeeded!
# 3. Verify connectivity to your SIP server (replace with your SIP server IP)
nc -zuv <your-sip-server-ip> 5060
# Expected: Connection succeeded (UDP) or similar positive responseIf step 2 fails, your firewall is blocking outbound TCP on port 443. This is the only outbound port the worker needs to reach CallMeter --- ask your network team to allow outbound TCP to gw.callmeter.io:443.
No Inbound Firewall Rules Required
The worker initiates all connections outbound to CallMeter's Worker Gateway at gw.callmeter.io:443. You do not need to open any inbound ports, configure port forwarding, or assign a public IP address to the host machine. The worker operates identically behind NAT, corporate firewalls, and restrictive network policies.
Step 1: Create a Worker in CallMeter
- Log in to callmeter.io and open your project
- Click Workers in the project sidebar
- Click Add Worker
- Fill in the worker details:
- Name --- A descriptive label for this worker (e.g., "DC-Frankfurt-01", "Lab-Worker", "Staging-NYC")
- Capacity --- The maximum number of concurrent SIP endpoints this worker should handle. Set this based on the host machine's resources (see Capacity and Scaling)
- Click Create
After creation, CallMeter generates a worker token and displays it on screen.
Step 2: Copy the Worker Token
Copy the Token Now --- It Will Not Be Shown Again
The worker token is displayed exactly once at creation time. Copy it immediately and store it securely. If you lose the token, you will need to regenerate it, which invalidates the previous token.
The token has this format:
cmw_a1b2c3d4e5f6... (68 characters total)The cmw_ prefix identifies it as a CallMeter worker token. The remaining 64 characters are a cryptographically random hex string.
Store this token securely:
- Use a secrets manager (HashiCorp Vault, AWS Secrets Manager, etc.)
- Or store it in an environment file with restricted permissions (
chmod 600) - Never commit tokens to version control
- Never share tokens in chat, email, or tickets
Step 3: Verify Network Access
Before deploying the container, confirm that the host machine can reach both CallMeter and your SIP infrastructure. This prevents confusing "container won't connect" issues later.
# Test outbound connectivity to the Worker Gateway
nc -zv gw.callmeter.io 443If this fails, check with your network team. The worker only needs outbound TCP to gw.callmeter.io on port 443. No inbound rules are required.
# Test connectivity to your SIP server (adjust IP and port)
nc -zuv 10.0.1.100 5060If this fails, the worker will connect to CallMeter but SIP tests will fail at the registration step. Ensure the host can reach your SIP infrastructure before proceeding.
Step 4: Deploy with Docker
Option A: Docker Run (Quick Start)
Where `<callmeter-worker-image>` comes from
Replace <callmeter-worker-image> with the image reference supplied with your worker license, and
authenticate to the registry with the credentials issued alongside it (docker login). Worker images are
distributed per-customer today, so there is no single public image name to copy.
Run a single command to start the worker:
mkdir -p ./cm_data && sudo chown 65532:65532 ./cm_data
docker run -d \
--name callmeter-worker \
--restart unless-stopped \
--network host \
-e WORKER_TOKEN=cmw_your_token_here \
-e SDP_IP=203.0.113.10 \
-v "$(pwd)/cm_data:/data" \
<callmeter-worker-image>The volume is not optional
Recordings are written to /data/recordings before they are uploaded. Without the -v mount they live on
the container's writable layer, which means two things: they grow unbounded against your Docker
storage, and any recording not yet uploaded is destroyed by docker compose down — including the
upgrade procedure below.
The container runs as uid 65532, so the host directory must be owned by it or the worker cannot
write. That is what the chown line does.
This command:
- Runs the container in detached mode (
-d) - Names it
callmeter-workerfor easy management - Restarts automatically unless explicitly stopped
- Passes your worker token as an environment variable
- Connects to the CallMeter Worker Gateway at
gw.callmeter.io:443
The worker publishes no ports. It makes a single outbound connection to the gateway and needs no inbound access.
Replace the Token
Replace cmw_your_token_here with the actual token you copied in Step 2. The token must start with cmw_ and be exactly 68 characters long.
Option B: Docker Compose (Recommended for Production)
Create a docker-compose.yml file for more manageable configuration:
version: "3.8"
services:
callmeter-worker:
image: <callmeter-worker-image>
container_name: callmeter-worker
restart: unless-stopped
network_mode: host
environment:
WORKER_TOKEN: "${WORKER_TOKEN}"
SDP_IP: "203.0.113.10"
volumes:
# Recording staging area. Must survive container restarts and image upgrades — see the warning
# above. Host dir must be owned by uid 65532.
- ./cm_data:/dataNo CPU or memory limits
These snippets deliberately set no deploy.resources.limits. Capacity is managed by CallMeter's
scheduler, which sizes and places work from the endpoint and media-weighted budgets it tracks for
your worker. A container-level cap fights that scheduler rather than assisting it: the platform
keeps dispatching to a worker it believes has room while the kernel throttles or OOM-kills the
processes doing the work.
Size the HOST for the capacity you configured, and let the scheduler do the rationing.
Sizing the recording volume
Recording bytes are dominated by video. Measured on production:
| Media | Per stream-hour |
|---|---|
| Audio | ~58 MB |
| Video (720p24) | ~675 MB |
A recording is staged locally only until it uploads, then deleted — so steady-state usage is roughly what one batch of concurrent recordings produces, not a running total. Provision for the largest test you intend to run: 10 concurrent video streams for one hour is ~7 GB of transient staging.
Allow headroom for uploads that cannot complete immediately. If the platform is unreachable, recordings accumulate until it returns; the worker retries on every reconnect, deletes each file once the platform confirms it, discards anything the platform permanently refuses, and sweeps abandoned files older than 24 hours.
Create a .env file alongside the compose file:
WORKER_TOKEN=cmw_your_token_here
# Where remote SIP endpoints send media. No default — the worker will not start without it.
SDP_IP=203.0.113.10Secure the .env file:
chmod 600 .envStart the worker:
docker compose up -dProtect Your .env File
The .env file contains your worker token. Ensure it is not readable by unauthorized users and is excluded from version control. Add .env to your .gitignore if this directory is tracked by git.
Step 5: Verify the Connection
After starting the container, verify the worker is connected and healthy.
Check Status in CallMeter UI
- Open your project in CallMeter
- Navigate to Workers
- Your worker should show status ONLINE with a green indicator
- The Last Heartbeat column should show a recent timestamp
The worker sends its first heartbeat within seconds of connecting. If the status does not change to ONLINE within 30 seconds, see the quick fixes below or the full Troubleshooting guide.
Quick fix if still OFFLINE after 30 seconds:
- Check if the container is running:
docker ps | grep callmeter-worker - Check logs for errors:
docker logs callmeter-worker 2>&1 | grep -i "error\|fail" - Verify the token:
docker exec callmeter-worker env | grep WORKER_TOKEN--- should be 68 characters starting withcmw_ - Test connectivity from inside the container:
docker exec callmeter-worker nc -zv gw.callmeter.io 443
Check Container Logs
View real-time logs to confirm successful connection:
docker logs callmeter-workerA successful startup sequence looks like:
[INFO] CallMeter Worker starting...
[INFO] Connecting to Worker Gateway at gw.callmeter.io:443
[INFO] Authentication successful
[INFO] Worker registered: dc-frankfurt-01
[INFO] Status: ONLINE
[INFO] Ready to accept test assignmentsRunning Your First Test with a User-Owned Worker
Once your worker shows ONLINE, you can run a test that executes entirely within your network.
- Create a new test in the same project where the worker is registered
- Configure a registrar --- add your SIP server's address, credentials, and transport protocol if you have not already
- Set up groups --- configure caller and callee groups with the SIP accounts, codecs, and media settings you want to test
- Select your worker --- in the test configuration, under Assignment, select Workers instead of Region, then pick your worker from the dropdown
- Set test parameters --- configure duration, endpoint count, and ramp-up period
- Click Run Test
The test executes entirely on your worker. SIP registration, call establishment, and RTP media all happen within your network. Only aggregated quality metrics are sent to CallMeter through the gateway connection.
Worker Must Be in the Same Project
Workers are scoped to the project where they are created. You can only select workers that belong to the same project as the test. If you do not see your worker in the dropdown, verify it was created in the correct project.
Quick fix if the test fails to start:
- "No available capacity" --- check the worker's capacity on the Workers page. The test requires more endpoints than the worker has available.
- SIP registration failures --- verify the worker can reach your SIP server:
docker exec callmeter-worker nc -zuv <sip-server-ip> 5060 - Worker shows OFFLINE --- the worker disconnected. Check logs and reconnect before retrying.
What Happens Behind the Scenes
When the container starts, the worker goes through this sequence:
- Initialization --- The worker process starts and reads its configuration from environment variables
- DNS resolution --- Resolves
gw.callmeter.ioto get the gateway's IP address - TLS connection --- Establishes an encrypted TCP connection to
gw.callmeter.io:443 - Authentication --- Presents the worker token. The gateway validates the token, verifies the associated license, and confirms no other worker is already connected with this token.
- Registration --- The gateway registers the worker and associates it with your organization and project. The worker appears in your dashboard.
- Heartbeats --- The worker sends a heartbeat every 30 seconds. If the gateway does not receive a heartbeat for 60 seconds, the worker is marked as stale.
- Ready --- The worker transitions to ONLINE and begins accepting test assignments.
If the connection drops at any point, the worker automatically reconnects with exponential backoff (5 seconds, then doubling up to 60 seconds). No manual intervention is required.
Common Deployment Patterns
Lab or Staging Environment
Deploy a single worker on a VM or spare machine in your lab network to validate SIP configurations before production:
docker run -d \
--name callmeter-lab \
--restart unless-stopped \
--network host \
-e WORKER_TOKEN=cmw_your_lab_token \
-e SDP_IP=10.0.1.50 \
<callmeter-worker-image>Datacenter Deployment
For production stress testing, deploy on a dedicated server with structured logging:
version: "3.8"
services:
callmeter-worker:
image: <callmeter-worker-image>
container_name: callmeter-dc-worker
restart: unless-stopped
network_mode: host
environment:
WORKER_TOKEN: "${WORKER_TOKEN}"
SDP_IP: "203.0.113.10"
SIP_IP: "10.0.1.50"
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "5"network_mode: host is recommended for every worker, not only large ones — it keeps docker-proxy off the media path and lets the address variables refer to the host's real interfaces. Set SIP_IP only when the host is multi-homed.
Customer Premises (MSP Pattern)
Managed service providers can deploy lightweight workers at customer sites. The worker only needs outbound internet access --- no inbound ports, no VPN tunnels:
docker run -d \
--name callmeter-customer-site \
--restart unless-stopped \
--network host \
-e WORKER_TOKEN=cmw_customer_site_token \
-e SDP_IP=192.168.10.20 \
<callmeter-worker-image>This pattern lets you test SIP quality from the customer's actual network perspective without any complex networking setup.
Multi-Worker Deployment
For higher capacity or geographic distribution, deploy multiple workers on the same or different machines.
Multiple Workers on One Machine
Each worker needs its own token, container name, staging directory and media port range:
version: "3.8"
services:
worker-1:
image: <callmeter-worker-image>
container_name: callmeter-worker-1
restart: unless-stopped
network_mode: host
environment:
WORKER_TOKEN: "${WORKER_TOKEN_1}"
SDP_IP: "203.0.113.10"
LOCAL_RTP_UDP_PORT_RANGE: "10000-19999"
volumes:
# Per-worker staging dir — never shared between workers.
- ./cm_data-1:/data
worker-2:
image: <callmeter-worker-image>
container_name: callmeter-worker-2
restart: unless-stopped
network_mode: host
environment:
WORKER_TOKEN: "${WORKER_TOKEN_2}"
SDP_IP: "203.0.113.10"
LOCAL_RTP_UDP_PORT_RANGE: "20000-29999"
volumes:
# Per-worker staging dir — never shared between workers.
- ./cm_data-2:/dataNon-Overlapping Port Ranges Are Required Here
With host networking these containers share the host's port space, and each worker tracks its own port usage without any visibility into its siblings. Left on the same range they will eventually pick the same port and one will fail to bind. Give each worker a range of its own — this is the one deployment where LOCAL_RTP_UDP_PORT_RANGE is not optional.
One Token Per Worker
Each worker must have its own unique token. A single token cannot be used by multiple workers simultaneously --- the second connection attempt will be rejected.
Workers Across Multiple Machines
Deploy the same Docker image on different hosts, each with its own token. Workers can be in different datacenters, cloud providers, or geographic locations. They all appear in your project's worker list and can be selected individually when creating tests.
Updating Workers
To update to the latest worker version:
# Pull the latest image
docker pull <callmeter-worker-image>
# Restart the container
docker compose down
docker compose up -dThe worker will reconnect to the gateway automatically after restart. Active test runs on the worker should complete before updating --- check the worker status in the UI and wait for any in-progress tests to finish.
Graceful Shutdown
When you stop a worker that has active endpoints, it enters the DRAINING state. Active calls complete normally before the container fully stops. The default Docker stop timeout (10 seconds) may not be sufficient for long-running tests --- consider using docker compose stop -t 300 to allow up to 5 minutes for draining.
Monitoring Workers
From the CallMeter Dashboard
The Workers page in your project shows all workers with their:
- Name and status (ONLINE, OFFLINE, DRAINING, ERROR)
- Current capacity usage (e.g., "35 / 100 endpoints")
- Last heartbeat timestamp
- Connection duration (uptime since last connection)
From the Host Machine
Monitor the Docker container directly:
# Container resource usage
docker stats callmeter-worker
# Recent logs
docker logs --tail 50 callmeter-worker
# Follow logs in real time
docker logs -f callmeter-workerIntegration with Monitoring Systems
The worker exposes nothing to poll --- there is no port to open and no probe to configure. Connection state is reported over the outbound gateway connection the worker already makes, so CallMeter is the authoritative source for whether a worker is alive: the Workers page shows the status and the Last heartbeat timestamp, and the gateway stops assigning work to a worker it has not heard from for 60 seconds.
On the host side, monitor the container itself with whatever container monitoring you already run:
- Container restart count increasing
- CPU and memory usage exceeding expected thresholds
ERRORorFATALlines in the container logs
Uninstalling a Worker
To remove a worker:
- Stop the container:
docker compose down - In CallMeter, navigate to Workers and delete the worker entry
- The token is now permanently invalidated
Deleting a worker in the UI does not stop the container --- always stop the container first to avoid orphaned connections.
Next Steps
- Configuration --- Full reference for all environment variables and tuning options
- Networking --- Firewall rules, port requirements, and NAT considerations
- Capacity and Scaling --- Size your workers correctly and scale horizontally
- Troubleshooting --- Diagnose connection issues, status problems, and test failures
Cloud Workers
How CallMeter's managed cloud workers operate, available regions, capacity by plan, and when cloud testing is sufficient.
Worker Configuration
Complete reference for all environment variables, SIP/media network settings, logging, token management, and performance tuning for CallMeter workers.