Skip to main content
CID222 Docs

Performance and resource problems

Containers killed for memory, services that restart, slow or missing detections, PDF exports that fail, and the GPU and OCR paths.

  • Version: 0.4
  • Role: admin_user
  • Type: troubleshooting

Almost every performance symptom on this appliance is one of three things: not enough memory, one service down, or a service that is up and cannot do its job because a model or a dependency is missing. The diagnostic snapshot separates them in one read.

Note

One critical finding usually explains several warnings. A host with no memory left produces one root cause and four symptoms, so work the highest-severity finding first rather than the loudest one.

Find your symptom

What you seeRunbook
A container exited with code 137A container was killed for memory
A container keeps restartingA container keeps restarting
One detection category returns nothingOne service is unhealthy
Detection is slow, or the whole appliance isThe machine is undersized
A filesystem is nearly fullA filesystem is nearly full
OCR returns nothing on an air-gapped applianceOCR has no models
Every PDF export failsA PDF will not render
Reviews never complete, or the analyst is unreachableThe analyst is unavailable
A file is refused, or its analysis failsA file is refused or its analysis fails
Lists are empty and nothing errorsThe database or Redis is unreachable
One dashboard action fails and nothing says whyA dashboard action fails

Memory and capacity

A container was killed for memory

Exit code 137 is the kernel, not the service. Restarting it restores service and changes nothing about why it was chosen.

Runbookperformance

A container was killed for running out of memory

You might see: was killed for running out of memory (exit 137) · exit code 137 · an OOM kill (exit 137) means this machine needs more RAM · the detector dies under load

Before you start

Checks

  1. 1

    Compare the host's total memory against the minimum for the licensed package

    diagnostics · host.memTotalMb = 32768

  2. 2

    Read how much memory the host has available right now

    diagnostics · host.memFreeMb = 2048

  3. 3

    Compare the killed container's declared limit with what it was using

    expected · Usage sits well below the limit. Usage at the limit means the limit is the constraint, not the host.

  4. 4

    Check whether every service running on this box is actually in use

    expected · Everything running is something this deployment needs.

A container keeps restarting

Separate an out-of-memory kill from a configuration the service refuses, before restarting it again.

Runbookperformance

A container keeps restarting

You might see: has restarted 12 times — it is in a crash loop · container restarting · a service comes back and dies again · the dashboard works for a minute and then errors

Before you start

Checks

  1. 1

    Check whether the last termination was an out-of-memory kill rather than a crash

    expected · oomKilled is false. When it is true, this is a sizing problem and not a crash.

  2. 2

    Read how much memory the host has left

    diagnostics · host.memFreeMb = 1024

  3. 3

    Verify the schema is current, since a failing boot migration restarts the gateway forever

    diagnostics · datastores.postgres.migrationsPending

  4. 4

    Check whether the container refuses its own configuration at boot

    expected · The service reports that it started and is listening, rather than refusing a setting.

The machine is undersized

The default compose profile declares roughly 42 GB of container memory limits, eleven services declare none, and the full sixteen-service stack was measured at about 30 GiB in use. Below the package minimum the kernel evicts whichever detection service is largest exactly when load peaks.

Runbookinstall

The machine has less RAM than the licensed package needs

You might see: This box has 16.0 GB of RAM; the package needs 32 GB · ML services will be OOM-killed under load · memory is 8192 MB; 16 GB is the designed size · the appliance runs but services keep being evicted

Before you start

Checks

  1. 1

    Compare total memory against the 32 GB minimum for the full stack

    diagnostics · host.memTotalMb = 32768

  2. 2

    For a deployment that processes documents and images, compare against the 64 GB recommendation

    diagnostics · host.memTotalMb = 65536

  3. 3

    Check whether the undersizing has already cost a service

    expected · No container has been killed for memory.

A filesystem is nearly full

An update needs room for a second copy of every image, and the appliance's state directory fails silently when the root filesystem fills — so a full disk shows up first as settings that do not persist.

Runbookinstall

A filesystem is nearly full

You might see: is 94% full (6 GB free of 100 GB) · an update needs room for a second copy of every image · no space left on device · the update fails part way through

Before you start

Checks

  1. 1

    Read the free space on the root filesystem

    diagnostics · host.disks[/].freeGb = 20

  2. 2

    Read the free space where Docker keeps its images and volumes

    diagnostics · host.disks[/var/lib/docker].freeGb = 40

  3. 3

    Read how large the CID database has grown

    diagnostics · datastores.postgres.dbSizeMb = 50000

Services

One service is unhealthy

Reachable-and-unhealthy is the interesting case, and on an air-gapped appliance it usually means a model that did not load. Read the dependency first for the analyst and the MCP server: they report themselves unhealthy when something they need is down, and fixing them wastes the window.

Runbookperformance

One service is unreachable or reports itself unhealthy

You might see: hap-guard-v2 is unreachable: no response · ocr-service answered but is not healthy · ml-detector is unreachable · one detector is down and the others are fine

Before you start

Checks

  1. 1

    Read whether the named service answered its health endpoint at all

    expected · reachable is true. When it is false the failure is network or container level, not model level.

  2. 2

    Read the container state for the named service

    expected · state is running, restarts are low and oomKilled is false.

  3. 3

    For a service that answers and reports itself unhealthy, read the error it returns

    expected · The error names what did not initialise — a model file, a device, a dependency.

  4. 4

    For the analyst and the MCP server, check what they depend on before blaming them

    expected · The dependency is healthy, so the unhealthy verdict is about this service.

OCR has no models

An image built before the model check shipped can carry the engine and not its models: healthy on a networked machine, which fetches them on demand, and fatal air-gapped. Restarting a service whose model files are absent changes nothing.

Note

When rebuilding the OCR image, CACHEBUST must stay declared in the compose file's build args — compose silently drops an undeclared build argument, so the pre-download layer is served from cache forever and a failed download looks permanent.

Runbookinstall

The OCR service has no models on an air-gapped appliance

You might see: HTTP 503: det/rec models not found · ocr-service answered but is not healthy · image analysis returns no text on the appliance and works on the dev box · OCR works when the machine has internet and fails when it does not

Before you start

Checks

  1. 1

    Read whether the OCR service reports itself healthy

    diagnostics · services[ocr-service].healthy = true

  2. 2

    Rule out an out-of-memory kill, which produces an unhealthy OCR service for a different reason

    expected · oomKilled is false.

GPU

A GPU is recommended, never required, and only for document, OCR and image-heavy work. There are two GPU paths and nothing else uses one: the CUDA compose overlay, and the gpu profile's Chandra-based OCR service. An appliance with no GPU is not misconfigured, and no finding fires for its absence — if OCR is slow on a CPU-only box, that is the documented behaviour, not a fault.

Reports and analysis

A PDF will not render

Rendering is a separate service the gateway calls over REPORT_RENDERER_URL. With it absent, every PDF export fails while everything else on the page keeps working — which is why it looks specific to one button.

Runbookperformance

A PDF or report will not render

You might see: Failed to render compliance scorecard PDF · Failed to render detections PDF · Failed to render executive summary PDF · Failed to render regulations PDF

Before you start

Checks

  1. 1

    Read whether the report renderer answered

    diagnostics · services[report-renderer].reachable = true

  2. 2

    Read whether the renderer reports itself healthy

    diagnostics · services[report-renderer].healthy = true

  3. 3

    Check whether only the large exports fail

    expected · The small export succeeds.

  4. 4

    For a compliance dossier, read whether the render was reconciled against the record

    expected · The renderer reported its rendered ids.

The analyst is unavailable

The analyst and its MCP server are opt-in and off by default (RISK_ANALYST_SERVICE_ENABLED). When enabled they are the largest thing on the box, the first thing memory pressure kills, and the slowest to come back.

Runbookperformance

The LLM risk analyst is disabled, unreachable, or times out

You might see: The risk analyst service is not enabled on this deployment. · The risk analyst could not be reached. · The analyst is already running an analysis. · The analysis did not start.

Before you start

Checks

  1. 1

    Read whether the gateway believes the analyst is enabled

    expected · The flag is set.

  2. 2

    Read whether the analyst service answered

    diagnostics · services[risk-analyst].reachable = true

  3. 3

    Read the MCP server, which the analyst reads the estate through

    diagnostics · services[mcp-server].reachable = true

  4. 4

    Check whether an analysis is already running

    expected · No analysis is in flight.

  5. 5

    For timeouts, compare the configured deadline against how long a reading actually takes

    expected · The deadline is longer than a typical completed review took.

A file is refused or its analysis fails

Size caps, unsupported formats and a document parser behind a circuit breaker all refuse in the same place. The circuit breaker is why the failure can look instant.

Runbookperformance

A file is refused, or its analysis fails

You might see: No file uploaded (expected multipart field "file") · Image size exceeds maximum allowed size of 10MB · Unsupported file type. Upload a PDF, DOCX or TXT. · legacy and macro-enabled spreadsheet formats cannot be safely redacted

Before you start

Checks

  1. 1

    Read whether the format is one the product accepts at all

    expected · The format is accepted on that surface.

  2. 2

    Read the size limit for that surface

    expected · The file is under the limit named in the message.

  3. 3

    Check whether the client was doing a chunked or resumable upload

    diagnostics · posture.resumableUploadPolicy = "block"

  4. 4

    Read whether document analysis is switched on for this deployment

    expected · The service is enabled.

  5. 5

    Read the document parser's own health

    diagnostics · services[document-parser].healthy = true

Last updated on