Skip to content

Infrastructure

This is the platform infrastructure — the AWS estate and the deploy control plane that runs it. It complements two neighbouring pages: How deployment works (how a change ships) and Observability & infrastructure (the application integrations — Sentry, GA4, S3, Redis, etc.). Everything here is as-built, current as of 2026-07-22.

Everything runs in one AWS account — 907815021625, region eu-central-1 (Frankfurt).

flowchart TB
subgraph GH["GitHub · OrascomDevelopment"]
  REPO[App repos]:::src
  GHA[GitHub Actions]:::ci
end
subgraph AWS["AWS · account 907815021625 · eu-central-1"]
  OIDC{{OIDC · 3 gh-deploy roles<br/>no long-lived keys}}:::sec
  CD[CodeDeploy<br/>Orascom-Prod-app · ODH-UAT · ODH-UAT-CRM<br/>ODH-Prod-Supervisor · ODH-Pdf]:::deploy
  S3REV[(S3 orascom-deploy-bucket<br/>revisions)]:::store
  ECR[(ECR backend-pdf-service)]:::store
  SM[Secrets Manager<br/>portals/&lt;env&gt;/&lt;app&gt; · CMK]:::sec
  subgraph COMPUTE["Compute — Graviton t4g behind the shared ALB + WAF"]
    BEASG[Backend ASG<br/>blue/green]:::compute
    CRMASG[CRM ASG<br/>blue/green]:::compute
    SUP[Supervisor box<br/>queue workers · scheduler · PDF container]:::compute
    AMP[Frontend · AWS Amplify]:::compute
  end
  RDS[(RDS MySQL · Multi-AZ)]:::data
  REDIS[(Redis EC2)]:::data
  MEDIA[(S3 media → CloudFront)]:::store
  OBS[CloudWatch alarms · CloudTrail · GuardDuty<br/>Access Analyzer · Teams notify Lambda]:::sec
end
REPO --> GHA -->|assume via OIDC| OIDC --> CD
GHA -->|build revision| S3REV --> CD
GHA -->|buildx arm64 push| ECR --> SUP
REPO -->|autoBuild| AMP
CD -->|blue/green| BEASG & CRMASG
CD -->|in-place| SUP
BEASG & CRMASG --> RDS
BEASG --> REDIS
BEASG --> MEDIA
CD -.render env.-> SM
BEASG & CRMASG & SUP -.-> OBS
classDef src fill:#e7eef6,stroke:#2563A8,color:#0b1b2b;
classDef ci fill:#efe7f7,stroke:#7A4FB5,color:#0b1b2b;
classDef deploy fill:#dff0f0,stroke:#0C7C84,color:#0b1b2b;
classDef compute fill:#f7efdc,stroke:#B7791F,color:#0b1b2b;
classDef data fill:#eceff1,stroke:#5B6B7A,color:#0b1b2b;
classDef store fill:#eceff1,stroke:#5B6B7A,color:#0b1b2b;
classDef sec fill:#f6e7dc,stroke:#C4622D,color:#0b1b2b;
Orascom Portals — AWS estate + deploy control plane (as-built)
  • Backend & CRM run on EC2 Auto Scaling groups (AWS Graviton t4g) behind one shared Application Load Balancer (orascom-prod-ecommerce-lb), with AWS WAF on the prod ALB. Both are blue/green in production and UAT (CodeDeploy stands up a fresh “green” ASG, health-checks it, shifts ALB traffic, then terminates the old “blue”); staging is a single in-place box.
  • The supervisor box (ODH-Supervisor in prod, ODH-UAT-Supervisor in UAT) is a stable pet, not an ASG. It runs, under supervisord: the backend queue workers (odh-queue), the Laravel scheduler (odh-schedule — its only home, so scheduled commands don’t duplicate across a scaled-out fleet), the pdf-service-queue worker, and the PDF service Docker container (see Storage & registry). Deploys to it are in-place.
  • Frontend = AWS Amplify (serverless static hosting), auto-building from GitHub Orascom-Portals-Frontend. Not on the CodeDeploy rail.
  • RDS MySQL, Multi-AZ (orascom-prod-rds) — the single shared relational store for backend and CRM. Automated backups + point-in-time recovery. Database migrations run once per deploy under a fleet-safe GET_LOCK mutex on this instance.
  • A dedicated Redis EC2 instance (ODH-Redis) for cache / queues / sessions where an environment routes them to Redis (the app also supports file/database drivers — see the Observability & infrastructure page).
  • S3 (media) → CloudFront — user/media assets, served via the CDN.
  • Amazon ECR — backend-pdf-service new 2026-07-21 — the private registry for the PDF service’s arm64 image. GitHub Actions cross-builds the image (buildx --platform linux/arm64) and pushes it here; the supervisor box pulls it during a PDF deploy. Lifecycle policy expires untagged images after 14 days.
  • S3 — orascom-deploy-bucket — holds the CodeDeploy revisions (self-contained deploy artifacts), env-prefixed (staging/, uat/, prod/).
  • GitHub Actions builds every deploy; AWS CodeDeploy rolls it out. No long-lived AWS keys exist in GitHub — Actions assumes one of three OIDC roles (gh-deploy-staging / gh-deploy-uat / gh-deploy-prod), each trust-bound to the exact repo:…:environment:<env> claim of the deploying repo.

  • CodeDeploy applications:

    App Groups Style Runs
    Orascom-Prod-app orascom-backend-deploy-group, orascom-crm-deploy-group blue/green prod backend + CRM
    ODH-Prod-Supervisor prod in-place prod queue workers/scheduler
    ODH-UAT backend + supervisor groups blue/green + in-place UAT backend + workers
    ODH-UAT-CRM uat in-place UAT CRM
    ODH-Pdf new 2026-07-21 uat, prod in-place PDF service container swap
    ODH-Staging-Backend / ODH-Staging-CRM staging in-place staging box
  • The PDF service has its own rail: push → buildx arm64 → ECRODH-Pdf in-place → a fail-safe container swap (build-and-smoke-test the new container, then swap — a bad image leaves the old one running). Full detail on How deployment works.

  • CloudWatch alarms — for both prod target groups: ALB 5xx and unhealthy-host count (prod-backend-ALB-* + prod-crm-ALB-*), plus backend CPU / memory (7 alarms today). A 5xx or unhealthy-host spike during a deploy drives auto-rollback, and they page one channel. Broader coverage from the planned floor — RDS storage/CPU/connections and queue-depth / failed-jobs custom metrics — is a follow-up, not yet stood up.
  • CloudTrail (odh-account-trail, multi-region, own encrypted bucket), GuardDuty, and IAM Access Analyzer (odh-analyzer) — the account-level audit/threat-detection floor, stood up before the Robusta handover.
  • Deploy notifications — every deploy posts a status card (✅ / ❌) to the “Orascom Portals Deployments” Microsoft Teams channel: backend & CRM (a notify step in GitHub Actions), frontend (an EventBridge rule on Amplify build events → the portals-amplify-teams-notify Lambda), the docs site (a GitHub Actions check_run workflow on the Cloudflare Pages build), and the PDF service (its own workflow). All share one card format and the portals/shared/notify-webhook secret.
  • Application error tracking/analytics (Sentry, GA4) is documented on the Observability & infrastructure page.
  • Root orascomdh.com is on Orascom IT’s Cloudflare; my.orascomdh.com is delegated to a Route53 zone. Moving that zone into this account (a one-time NS delegation flip by IT) is the one remaining DNS task — bounded by the media.my.orascomdh.com ACM cert expiry (2026-10-29). See the handoff tracker for status.
  • ACM provides the TLS certs for the *.my.orascomdh.com family (ALB + CloudFront).
  • GitLab (CI + runners) — fully retired, zero runtime role.
  • AWS CodePipeline (3 pipelines) — disconnected, kept as a short-term rollback lever, then delete.
  • DigitalOcean staging + *.robustastudio.com hosts — slated for retirement once DNS + external callback re-registrations complete (tracked separately).
  • Robusta-hosted tooling (SonarQube, Dependency-Track, self-hosted Sentry, Grafana, Mattermost) — replaced by GitHub-native quality gates + CloudWatch + Orascom’s own Sentry as the handover completes.