CallMeter logoCallMeter Docs

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.io on port 443 (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 response

If 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

  1. Log in to callmeter.io and open your project
  2. Click Workers in the project sidebar
  3. Click Add Worker
  4. 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)
  5. 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 443

If 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 5060

If 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-worker for 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.

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:/data

No 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:

MediaPer 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.10

Secure the .env file:

chmod 600 .env

Start the worker:

docker compose up -d

Protect 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

  1. Open your project in CallMeter
  2. Navigate to Workers
  3. Your worker should show status ONLINE with a green indicator
  4. 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:

  1. Check if the container is running: docker ps | grep callmeter-worker
  2. Check logs for errors: docker logs callmeter-worker 2>&1 | grep -i "error\|fail"
  3. Verify the token: docker exec callmeter-worker env | grep WORKER_TOKEN --- should be 68 characters starting with cmw_
  4. 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-worker

A 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 assignments

Running Your First Test with a User-Owned Worker

Once your worker shows ONLINE, you can run a test that executes entirely within your network.

  1. Create a new test in the same project where the worker is registered
  2. Configure a registrar --- add your SIP server's address, credentials, and transport protocol if you have not already
  3. Set up groups --- configure caller and callee groups with the SIP accounts, codecs, and media settings you want to test
  4. Select your worker --- in the test configuration, under Assignment, select Workers instead of Region, then pick your worker from the dropdown
  5. Set test parameters --- configure duration, endpoint count, and ramp-up period
  6. 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:

  1. Initialization --- The worker process starts and reads its configuration from environment variables
  2. DNS resolution --- Resolves gw.callmeter.io to get the gateway's IP address
  3. TLS connection --- Establishes an encrypted TCP connection to gw.callmeter.io:443
  4. 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.
  5. Registration --- The gateway registers the worker and associates it with your organization and project. The worker appears in your dashboard.
  6. 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.
  7. 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:/data

Non-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 -d

The 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-worker

Integration 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
  • ERROR or FATAL lines in the container logs

Uninstalling a Worker

To remove a worker:

  1. Stop the container:
    docker compose down
  2. In CallMeter, navigate to Workers and delete the worker entry
  3. 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

On this page