Skip to main content

Scaling

Face Matcher services are designed to run on a single core each. The biometric engine and the ONNX runtime are pinned to one thread by default (IFaceConfiguration__ThreadNum=1 and OnnxRuntimeConfiguration__Solver*Threads=1 in .env, sections 2.10 and 2.11), so a service does not grow its CPU usage with the load. When a service becomes a bottleneck you add replicas of it. This keeps every container predictable and makes capacity a matter of counting containers.

Sizing samples for typical camera counts and traffic levels are summarised in Hardware requirements. Use them as a starting point and confirm the numbers with the sizing recommendation Innovatrics provides for your project.

What to scale​

ServiceRoleWhen to add replicas
cam-NOne container per RTSP stream; detects and tracks faces on its own streamOne per camera, always
detectorFace detection for API requests such as enrollment and image searchMany enrollments or image searches run in parallel
extractorCreates face templates from detected facesThe main throughput service; scales with persons per minute
matcherMatches templates against watchlistsLarge watchlists or high traffic (Matching__ThreadCount=4 in .env is the per-instance thread count)
livenessPassive liveness on facesOnly when liveness is enabled; scales with matched faces per minute

Camera containers do their own face detection, so the standalone detector is not on the per-frame path and one instance is usually enough. The extractor is normally the first service to saturate, because every detected face produces an extraction request. base, api, graphql-api, streamdatadbworker and the face search service run as single instances. Services that run on the GPU need fewer replicas; see GPU acceleration.

Add replicas​

Add a deploy.replicas entry to the service. Put it in docker-compose.override.yml, which is not replaced when you unpack a new release package, so your replica counts survive upgrades:

services:
extractor:
deploy:
replicas: 4
matcher:
deploy:
replicas: 2

Replicated services must not have a container_name or published ports. The engine services in the release package meet this condition; Compose names the instances face-matcher-extractor-1, face-matcher-extractor-2 and so on. Apply the change from the deployment directory:

docker compose up -d

Add camera containers​

The package ships five camera slots (cam-1 to cam-5) and seeds five camera services in the database (CameraServicesCount=5 in .env, passed to the database migration as -p). To process a sixth stream:

  1. Copy the cam-5 block in docker-compose.yml as cam-6 and change every 5 to 6: the command becomes --serviceName SFCam6 and the preview port 30006:30006 (preview ports are 30000 + camera sequence). Add matching restart: unless-stopped and user: root entries for cam-6 in docker-compose.override.yml.
  2. Set CameraServicesCount=6 in .env.
  3. Add CAM_PREVIEW_HOST_SFCAM6=cam-6 to .env.station so Station can show the preview.
  4. Run ./start.sh. It re-runs the database migration with the new count, which seeds the camera record, and restarts all services.

Apply the same pattern for any number of cameras: cam-35 uses SFCam35 and port 30035. The -p argument of run-migration is the number of camera services, not a parallelism setting; run.sh reads it from CameraServicesCount, so you do not edit the script. If one host runs out of capacity, spread the camera containers across hosts as described in Multi-server deployment.