Dasnuve · Cloud Discovery

Source Code Recovery & Reliability Triage

What is running on the Scrivas EC2 estate, where the source lives, and why production is fragile.
Account 716468089330
Region us-east-2 · 7 instances
2026-08-28 · CONFIDENTIAL
10
Repos recoverable on EC2
Full git history, not just build output
2
Frontends not recoverable
Deployed build only — no .git
23
Prod containers, one host
Incl. 3 × Postgres, Kafka, OpenSearch
0
Memory limits set
And zero swap, on all 7 instances
01

The source code is recoverable — and that is time-sensitive

Scrivas_dev_env · ML_dev
All 10 application repositories exist as complete git checkouts on Scrivas-owned instances, with full commit history — not merely deployed artifacts. Scrivas can recover its codebase from its own AWS account today.

Every repository points at a single external remote: git@git.devteam.space:scrivas/<repo>.git — the incumbent contractor's self-hosted GitLab, which Scrivas does not control. The on-instance checkouts are therefore the client's only independent leverage over their own source.

APP

Scrivas_dev_env /home/admin/

RepositoryCommitsLast commit
scrivas_backend13802026-08-28 · Vasilii
scrivas_gate1832026-08-14 · Vasilii
scrivas_search962026-06-24 · Azamat
patient592026-08-13 · vturtugeshev
ML

ML_dev /srv/

RepositoryCommitsLast commit
post_processor1372026-06-30
sai_suggestions932026-06-16
ml_monitoring622026-07-20
patient-summary-service492026-06-12
patient_document_parser412026-06-16
soniox_transcriber252026-06-16
The gap: /var/www/scrivas_frontend and /var/www/Scrivas_admin_frontend are deployed build output with no .git. Frontend source cannot be recovered from EC2 and must be obtained from the contractor's GitLab.

Why this is urgent: scrivas_backend received a commit on the day of this assessment. Development is active on infrastructure the contractor controls. Mirror all 10 repos (git clone --mirror) to Scrivas-controlled storage before any contract conversation changes access posture.

02

Two tiers, two engineering standards

the better pattern already exists in-house
APP

Application tier fragile

23 containers on a single hostencounter_api, gate_api, patient_api, 8 Celery workers, celery_beat, 3 × Postgres 17.5, Redis, Kafka, Debezium CDC, OpenSearch, autoheal.

Deployed by docker-compose directly from git checkouts. No orchestration, no image registry, no immutable artifact.

ML

ML tier mature

Deployed from ECR (…dkr.ecr.us-east-2…/scrivas/*) with commit-SHA image tags and blue/green slots (dev-green-*, stage-blue-*).

Health checks defined and passing across every service. This is a materially stronger delivery pipeline — and a template the application tier can adopt.

03

Reliability findings

ranked by likely contribution to reported instability
#FindingEvidenceRisk
1No memory limit on any prod containerHostConfig.Memory=0 on all 23High
2No swap on any instanceswapon --show empty, all 7High
3Prod at 73% memory at rest11 GiB used of 15 GiBHigh
4Whole stack on one host, incl. 3 databasesdocker ps — 23 containersHigh
53 prod containers set to never restartKafka, OpenSearch, search-api — policy noHigh
6Prod OS differs from dev/stageUbuntu 26.04 vs 24.04 elsewhereMedium
7autoheal deployed in productionwillfarrell/autoheal:1.2.0Medium
8Uncommitted config drift in prod checkoutsdocker-compose.yml.bkp, .env.saveMedium
947 interactive login sessions on produptime (32 on Ml_prod)Medium
10sai-suggestions-redis.service failedML_stage systemctl --state=failedLow
04

Primary hypothesis: unbounded memory on a shared host

Production memory, at rest — before any workload spike:

11 GiB used (73%) 3.5 GiB cache 441 MiB free 0 B swap
Findings 1–4 compound into a single failure mode. Production runs 23 containers — including three Postgres instances, Kafka and OpenSearch — on one 15 GiB host already 73% consumed at rest, with no per-container memory limits and no swap. A single Celery worker handling an oversized document or audio chunk can exhaust host memory. With no limits, the kernel OOM killer selects by resident size — so it will typically kill a database, not the worker that caused the problem.

That yields precisely the profile a client reports as "random reliability issues": unpredictable, uncorrelated with the triggering workload, and difficult to reproduce.

Finding 5 compounds recovery. Kafka, OpenSearch and search-api carry restart policy no — if they die or the host reboots, they stay down until someone intervenes. Everything else is unless-stopped, so a reboot yields a partially-recovered stack that looks healthy from the outside.

Finding 7 corroborates. Someone deliberately deployed autoheal, a daemon whose only job is restarting containers that fail health checks. That treats a symptom — and indicates health-check failures were frequent enough to be worth automating around.

What the evidence does not show — stated plainly, because it bounds the conclusion.

· No OOM kill was captured. dmesg and journalctl on prod show no OOM events in the retained window; State.OOMKilled is false on all 23 containers.

· No crash-looping. RestartCount is 0 on every prod container. The Celery workers showing "Up 6 hours" were redeployed at 13:49 that day, not restarted by failure.

· last is not installed on prod, so reboot history could not be read there.

Findings 1–5 are therefore a demonstrated structural exposure, not an observed root cause. Confirming the hypothesis requires history these instances do not currently retain — which is itself a finding.

05

Recommended next steps

NOW

Immediate

  1. Mirror all 10 repos to Scrivas-controlled storage. Highest value, lowest effort, time-sensitive.
  2. Set unless-stopped on Kafka, OpenSearch and search-api — removes the silent partial-recovery failure.
PROVE

Confirm the hypothesis

  1. Enable per-container memory metrics in CloudWatch — EC2-CloudWatchAgent-Role is already attached.
  2. Add mem_limit per service, sized from observed peaks; add swap as an OOM buffer.
  3. Query historic mem_used_percent to date the onset of instability.
FIX

Structural

  1. Move the three Postgres instances off the shared application host.
  2. Align prod OS/AMI with stage — 26.04 vs 24.04 makes staging an unreliable rehearsal for production.
  3. Extend the ML tier's pattern — ECR, commit-SHA tags, blue/green — to the application tier. The better pattern already exists in-house.
  4. Reconcile config drift (.env.save, docker-compose.yml.bkp) into version control.
  5. Audit the 47 interactive prod sessions.