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
| Service | Role | When to add replicas |
|---|---|---|
cam-N | One container per RTSP stream; detects and tracks faces on its own stream | One per camera, always |
detector | Face detection for API requests such as enrollment and image search | Many enrollments or image searches run in parallel |
extractor | Creates face templates from detected faces | The main throughput service; scales with persons per minute |
matcher | Matches templates against watchlists | Large watchlists or high traffic (Matching__ThreadCount=4 in .env is the per-instance thread count) |
liveness | Passive liveness on faces | Only 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:
- Copy the
cam-5block indocker-compose.ymlascam-6and change every5to6: the command becomes--serviceName SFCam6and the preview port30006:30006(preview ports are30000 + camera sequence). Add matchingrestart: unless-stoppedanduser: rootentries forcam-6indocker-compose.override.yml. - Set
CameraServicesCount=6in.env. - Add
CAM_PREVIEW_HOST_SFCAM6=cam-6to.env.stationso Station can show the preview. - 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.