MCMMCM DocsEngineering (Internal)
Deployment
v1.2 is unreleased — see v1.1 for the current stable release.

VM Sizing Calculation Guide

Internal engineering reference documenting empirical telemetry measurements, per-module data footprint calculations, and formula derivations for MCM platform VM sizing.

MCM Platform — VM Sizing Calculation Guide

Confidential — For Internal Operations and RevDau Engineering Teams Only. This document contains the raw telemetry measurements, per-module analysis, formula derivations, and empirical sizing data used to produce the customer-facing VM Sizing Guide (published in mcm-website at /docs/v1.1/vm-sizing/vm-sizing-guide).


1. Telemetry Methodology

1.1 Test Environment

All measurements were collected from a live MCM Platform deployment on the following host:

ParameterValue
Instance TypeAWS EC2 (Intel Xeon E5-2686 v4 @ 2.30GHz)
vCPUs8 (1 socket, 8 cores, 1 thread/core)
Total RAM32 GB
Root Disk96 GB (/dev/root)
OSUbuntu Linux 7.0.0-1006-aws (x86_64)
DockerAll 22 containers running (Full profile deployment)

1.2 Collection Method

Telemetry was collected using the mcm-telemetry.sh script (/opt/mcm/scripts/mcm-telemetry.sh) which queries:

  • docker stats --no-stream for container CPU/memory snapshots.
  • /proc/1/status inside each Java container for VmRSS (resident set size).
  • mongosh aggregate queries using $bsonSize on $$ROOT to calculate exact document sizes.
  • Elasticsearch and Wazuh Indexer REST APIs for cluster health and index sizes.
  • du -sh on Steampipe cache directories inside the discovery container.
  • docker system df -v and du -sh /var/lib/docker/volumes/* for volume measurements.

1.3 Test Runs

Two telemetry snapshots were taken:

RunTimestamp (UTC)EnterprisesMSPsCloud AccountsDiscovered ResourcesModules Active
Run 1 (Baseline)2026-06-22 17:51
111 AWS266Discovery
Run 2 (Scaled)2026-06-22 18:15
212 AWS + 1 Ubuntu15,854 (582 real + 15,272 system catalog)Discovery, Governance, Orchestration, FinOps, SecOps

2. Container Memory Analysis

2.1 Baseline RAM Consumption (All Containers)

The table below compares container memory usage between the baseline run (1 account idle) and the scaled run (3 accounts after discovery + module operations):

ContainerRun 1 (Baseline)Run 2 (Active)DeltaNotes
mcm-api832 MiB922 MiB+90 MiBCore API grows with active sessions and request caches
mcm-discovery-api1,312 MiB (1.28 GiB)2,904 MiB (2.84 GiB)+1,592 MiBSteampipe queries hold large result sets in JVM heap
mcm-governance-api664 MiB704 MiB+40 MiBPolicy evaluation engine loads rules per tenant
mcm-finops-api626 MiB649 MiB+23 MiBMinimal growth — no cost data ingested yet
mcm-secops-api791 MiB823 MiB+32 MiBWazuh API client connections and alert caches
mcm-orchestration-api636 MiB648 MiB+12 MiBStack definitions and OpenTofu state caching
mcm-observability-api617 MiB615 MiB-2 MiBStable — no active agent telemetry ingested
keycloak775 MiB777 MiB+2 MiBStable — session management overhead is minimal
mongodb96 MiB179 MiB+83 MiBWiredTiger cache grew from 2 MB to 53 MB with data
elasticsearch1,997 MiB (1.95 GiB)2,048 MiB (2.0 GiB)+51 MiBFilebeat index growth (17K to 20K docs)
wazuh.indexer1,495 MiB (1.46 GiB)1,510 MiB (1.47 GiB)+15 MiBMinimal alert indexing (189 alerts)
wazuh.manager756 MiB628 MiB-128 MiBGC reclaim cycle — high I/O (259 GB Block writes)
apisix239 MiB241 MiB+2 MiBStable reverse proxy with route cache
mcm-ui77 MiB81 MiB+4 MiBStatic Next.js server, negligible growth
mcm-ai-api119 MiB119 MiB0Python FastAPI idle
keycloak-postgres36 MiB37 MiB+1 MiBStable
etcd16 MiB16 MiB0Stable route config store

2.2 JVM Process Memory (VmRSS from /proc/1/status)

ContainerVmRSS (Run 1)VmRSS (Run 2)DeltaVmPeak
mcm-api854 MB943 MB+89 MB14.0 GB (virtual)
mcm-governance-api683 MB723 MB+40 MB13.9 GB
mcm-finops-api645 MB669 MB+24 MB13.9 GB
mcm-secops-api811 MB842 MB+31 MB13.9 GB
mcm-orchestration-api649 MB661 MB+12 MB13.9 GB
mcm-discovery-api830 MB2,254 MB+1,424 MB13.9 GB
mcm-observability-api637 MB635 MB-2 MB13.9 GB
keycloak778 MB780 MB+2 MB27.8 GB (virtual)

Key Finding — Discovery API Memory Scaling: The mcm-discovery-api container is the only service that exhibits significant memory growth under load. During active discovery of 3 cloud accounts (582 real resources), its VmRSS jumped from 830 MB to 2,254 MB (+1.4 GB). This is caused by Steampipe holding query result sets and resource metadata in JVM heap during bulk discovery sweeps.

Empirical RAM per Discovery Account: 1,424 MB growth / 2 new accounts = ~712 MB per actively polled account during discovery. Post-discovery the memory may settle after GC cycles.

2.3 Derived RAM Sizing Formula

Based on measurements:

Total RAM (GB) =
    Host OS & Docker Daemon ............... 2 GB
  + Core Platform APIs (mcm-api + keycloak
    + apisix + etcd + postgres) ........... 2.2 GB baseline
  + MongoDB (WiredTiger cache) ............ 2 GB minimum (scales to 50% of allocated RAM)
  + Elasticsearch ......................... 2 GB heap
  + Per Business Module ................... 0.7 GB baseline each
  + Discovery Module (active polling) ..... 0.8 GB baseline + 0.7 GB per concurrent account
  + Wazuh Stack (if SecOps enabled) ....... 3.5 GB (manager + indexer + dashboard)
  + Observability (if enabled) ............ 1.5 GB (fleet-server + kibana + filebeat)

Example — Standard (5 accounts, all business modules, no SecOps):

= 2 + 2.2 + 2 + 2 + (4 x 0.7) + (0.8 + 5 x 0.7)
= 2 + 2.2 + 2 + 2 + 2.8 + 4.3
= 15.3 GB → Recommend 16 GB minimum, 32 GB comfortable

3. MongoDB Storage Analysis — Per Module

3.1 Database-Level Summary

MetricRun 1 (266 resources)Run 2 (15,854 resources)
Data Size (uncompressed BSON)0.71 MB42.73 MB
Storage Size (WiredTiger compressed on-disk)0.66 MB8.77 MB
Index Size0.66 MB1.58 MB
Total On-Disk (storage + indexes)1.32 MB10.36 MB
WiredTiger Compression Ratio~1.08x~4.87x
Avg Data per Resource (BSON)2.73 KB2.76 KB
Avg On-Disk per Resource5.08 KB0.67 KB

Key Finding — WiredTiger Compression: At higher data volumes, WiredTiger's block compression becomes dramatically more effective. The compression ratio improved from 1.08x (266 resources) to 4.87x (15,854 resources). This means per-resource on-disk cost drops significantly at scale.

3.2 Per-Collection Footprint (from Tenant Breakdown)

Discovery Module (cloud_resources_v2)

MetricEnterprise 1 (1 account)Enterprise 2 (2 accounts)SYSTEM catalog
Document Count26631615,272
Total BSON Size390.94 KB509.02 KB42,157.63 KB (41.2 MB)
Avg BSON per Doc1.47 KB1.61 KB2.76 KB

Breakdown by Resource Type (from Run 2):

AWS Resource TypeCountNotes
AWS_INSTANCE_TYPE14,252System catalog data (region-specific instance specs)
AWS_AMI1,020System catalog data (AMI images)
AWS_SECURITY_GROUP119Real tenant resources
AWS_SUBNET118Real tenant resources
AWS_KEY_PAIR67Real tenant resources
AWS_ROUTE_TABLE62Real tenant resources
AWS_NACL58Real tenant resources
AWS_VPC51Real tenant resources
AWS_INTERNET_GATEWAY41Real tenant resources
AWS_S319Real tenant resources
AWS_EBS_VOLUME12Real tenant resources
AWS_IAM_POLICY12Real tenant resources
AWS_IAM_USER11Real tenant resources
AWS_EC29Real tenant resources
AWS_EBS_SNAPSHOT3Real tenant resources

Important Note — SYSTEM Catalog Resources: The discovery module stores global catalog data (e.g., all AWS instance types and AMIs per region) under a SYSTEM tenantId with accountId: GLOBAL. These are not customer resources but reference data. In this deployment, system catalog constitutes 15,272 out of 15,854 total resources (96.3%). Only 582 resources are actual customer-discovered infrastructure.

Per-Account Discovery Data (real resources only):

AccountProviderResourcesBSON SizeAvg per Resource
demoAWS266390.94 KB1.47 KB
qewAWS316509.02 KB1.61 KB

Derived: Average real resource BSON size = ~1.5 KB/resource

Governance Module

CollectionDocs per TenantTotal BSON per TenantAvg per DocGrowth Pattern
governance-policies287205.67 KB0.72 KBSeeded on tenant creation, grows with custom policies
governance_host_policies8157.52 KB0.71 KBSeeded, grows with host-specific overrides
Governance_groups61.72 KB0.29 KBStatic after creation
Total per Tenant374~265 KBMostly static seed data

Derived: Governance baseline = ~265 KB per tenant (fixed seed) + ~0.72 KB per custom policy added

Orchestration Module

CollectionDocs per TenantTotal BSON per TenantAvg per DocGrowth Pattern
Orchestration-stackfile6321.55 KB0.34 KBSeeded template files
Orchestration-stacks2126.97 KB1.28 KBGrows with deployed stacks
orchestration_data (volume)167 KB (volume)OpenTofu binaries and state files
Total per Tenant84~48.5 KBMostly static seed data

Derived: Orchestration baseline = ~48.5 KB per tenant + ~1.3 KB per additional deployed stack

Orchestration Docker Volume: The mcm_orchestration_data volume grew from 83.5 KB (Run 1) to 167 KB (Run 2) as new stack executions cached OpenTofu providers. At scale with many IaC deployments, expect this volume to grow to 50-200 MB.

FinOps Module

CollectionDocs (Tenant 1)Docs (Tenant 2)Notes
upcoming-billing-plan35Billing cycle forecasts
enterprise-plan15Subscription plans
recommendations010 (21.86 KB)Cost optimization recommendations
Elasticsearch Indices
cloud-cost-index0 docs0 docsEmpty — no CUR data ingested yet
subscription-cost-index0 docs0 docsEmpty — no subscription data yet
cloud-forecast-index0 docs0 docsEmpty — no forecast data yet

Note: FinOps stores billing data primarily in Elasticsearch indices (cloud-cost-index, subscription-cost-index), not in MongoDB. The MongoDB collections store only plan metadata and recommendations. Empirical FinOps sizing requires ingesting actual CUR/billing data, which was not performed in this test run.

Estimated FinOps Growth (from prior engineering analysis):

  • Small account (< 100 resources, < $10K/month spend): ~50 MB/year in Elasticsearch indices.
  • Large account (1,000+ resources, > $100K/month spend): ~500 MB to 2 GB/year.
  • CUR Parsing Buffer: Temporary disk spike of 1-5 GB during large CSV file ingestion.

SecOps Module (Wazuh)

ComponentMeasurementNotes
Wazuh Alerts Index189 alerts, 597.7 KBMinimal — only Wazuh Manager self-monitoring
Wazuh Queue Volume11.78 GBEvent queue buffer — largest single volume
Wazuh Logs Volume4.2 MBOperational logs
Wazuh Indexer Data Volume833 KB → 2.1 MBOpenSearch index storage
Wazuh Manager Block I/O5.09 GB read / 259 GB writtenExtremely high write I/O from event processing

Key Finding — Wazuh Queue is the Largest Storage Consumer: Even with no external agents enrolled, the mcm_wazuh_queue volume consumed 11.78 GB within 7 hours of operation. This is the Wazuh Manager's internal event processing queue. With real agent telemetry, this will grow significantly faster.

Derived SecOps Disk Formula:

SecOps Disk (GB) = Wazuh Queue Baseline (12 GB) + (Enrolled Agents * 3 GB/month * Retention Months) + Wazuh Indexer Indices

Observability Module

ComponentMeasurementNotes
Elasticsearch indices~47 MB total (filebeat + metrics + logs + fleet)APM/monitoring data
Fleet Server RAM404 MiBElastic Agent daemon
Filebeat RAM80 MiBLog collection agent
Kibana RAM666 MiBDashboard UI

Key Finding — Filebeat Index Growth: The filebeat-8.19.3 index grew from 30.2 MB (Run 1, 17,322 docs) to 33.5 MB (Run 2, 20,549 docs). Growth rate: ~3.3 MB for 3,227 new log entries = ~1 KB per log entry.

Core Platform (mcm-api)

CollectionDocs per TenantTotal BSON per TenantNotes
dashboards10.30 KBDefault dashboard
dashboard_tabs40.90 KBDashboard tab definitions
dashboard_widgets77.90 KBWidget configurations
enterprise10.62–0.68 KBEnterprise record
accounts1-21.76–3.28 KBOnboarded cloud accounts
executionHistories5-95.83–7.93 KBModule execution logs
Total Core per Tenant~20~18 KBMinimal baseline

MSP Tenant Footprint:

CollectionDocsBSON SizeNotes
billing_plans52.07 KBMSP billing plan definitions
msp10.24 KBMSP entity
onboarding_steps52.29 KBStep tracker
Total MSP11~4.6 KBVery lightweight

4. Steampipe Cache Analysis

MetricRun 1 (1 account)Run 2 (3 accounts)Delta
.steampipe directory size824 MB830 MB+6 MB
Accounts onboarded1 AWS2 AWS + 1 Ubuntu+2

Breakdown:

  • Static Baseline: ~818 MB consumed immediately upon container initialization. This includes:
    • Steampipe internal PostgreSQL database and WAL files.
    • AWS plugin binary and schema definitions.
    • FDW (Foreign Data Wrapper) shared objects.
  • Per-Account Incremental: ~3 MB per onboarded AWS account (connection config, cached credentials, query result caches).
  • Note: The Ubuntu account does not use Steampipe (it is not a cloud provider), so its incremental footprint is negligible.

Derived: Steampipe Cache = 820 MB baseline + ~3-10 MB per cloud account


5. Elasticsearch & Wazuh Indexer Analysis

5.1 Elasticsearch (Observability + FinOps)

Index CategorySize (Run 2)DocsGrowth Driver
filebeat-8.19.3-*33.5 MB20,549Container log shipping — grows with platform activity
metrics-elastic_agent.*~9.2 MB~5,690Agent self-monitoring metrics
logs-elastic_agent.*~2.7 MB~6,538Agent operational logs
fleet_server.*~0.75 MB~900Fleet agent status tracking
cloud-cost-index249 bytes0Empty — awaits FinOps CUR ingestion
subscription-cost-index249 bytes0Empty — awaits subscription data
Total ES Storage~47 MB~33,677Dominated by filebeat logs

ES Cluster Status: yellow — 60 active shards, 17 unassigned replica shards (expected on single-node). No data loss risk.

ES JVM Heap:

  • Heap Used: 211 MB (19% of 1 GB max) at Run 2.
  • Non-Heap: 264 MB (Metaspace + code cache).

5.2 Wazuh Indexer (OpenSearch)

IndexSizeDocsNotes
wazuh-alerts-4.x-2026.06.22597.7 KB189Self-monitoring alerts only
.opendistro_security44.2 KB10Security configuration
wazuh-states-* (15 indices)~40 KB total0Empty state indices
Total~680 KB199Minimal — no external agents enrolled

Cluster Status: green — 21 shards, all assigned, 100% healthy.


6. Docker Volume Sizing Analysis

VolumeSize (Run 1)Size (Run 2)DeltaGrowth Driver
mcm_wazuh_queue11.79 GB11.78 GB~0Wazuh event queue (dominant)
mcm_mongodb_data320 MB330 MB+10 MBMongoDB data files
mcm_etcd_data145 MB145 MB0APISIX route config (stable)
mcm_es_data110 MB110 MB~0Elasticsearch indices
mcm_keycloak_db_data72 MB72 MB0Keycloak PostgreSQL (stable)
mcm_wazuh_logs4.15 MB4.21 MB+0.06 MBWazuh operational logs
mcm_wazuh_etc2.83 MB2.83 MB0Wazuh config (stable)
mcm_wazuh-indexer-data887 KB2.1 MB+1.2 MBOpenSearch alert indices
mcm_orchestration_data84 KB167 KB+83 KBOrchestration state/plans
Total Volumes~12.44 GB~12.44 GBDominated by Wazuh queue

Key Finding: The mcm_wazuh_queue volume alone accounts for ~95% of all Docker volume storage. Even without external agent enrollment, the Wazuh Manager generates a significant event queue from self-monitoring.


7. Docker Image Sizes

Total Docker image storage: 27.49 GB

CategoryImagesTotal Size
MCM Services (UI + 7 Java APIs + AI)9~13.8 GB
Databases (MongoDB + ES + Postgres)3~3.6 GB
Security Stack (Wazuh Manager + Indexer + Dashboard)3~6.7 GB
Observability (Elastic Agent + Kibana + Filebeat)3~5.3 GB
Infrastructure (APISIX + etcd + yq + Keycloak)4~1.6 GB

Derived: Base Docker image storage = ~28 GB (required before any data volumes)


8. Host Disk Utilization Summary

ComponentSizeNotes
Docker Images27.49 GBAll 22 images loaded
Docker Containers (writable layers)591 MBContainer filesystem deltas
Docker Volumes (all)12.44 GBWazuh queue dominates
Steampipe Cache (inside container)830 MBInside mcm-discovery-api container writable layer
Total Docker Consumed~41.4 GB
OS + System~17 GBUbuntu base + packages
Total Host Used58 GB of 96 GB61% utilization

9. Final Derived Formulas

9.1 Host Disk Formula (Refined)

Total Disk (GB) =
    OS + Docker Engine + Images ................ 45 GB (fixed baseline)
  + Steampipe Baseline Cache ................... 0.8 GB (fixed)
  + Steampipe per Account ...................... Accounts * 0.01 GB
  + MongoDB Data ............................... Resources * 0.005 MB (5 KB/resource compressed)
  + MongoDB System Catalog ..................... 0.04 GB (fixed one-time per AWS region)
  + Elasticsearch (Observability) .............. 0.05 GB baseline + 0.001 GB/day log growth
  + Orchestration State ........................ 0.2 GB (grows with deployed stacks)
  + FinOps Cost Indices ........................ Accounts * 0.5 GB/year (ES indices)
  + FinOps Parsing Buffer ...................... 5 GB (temporary during ingestion)
  + Wazuh Queue ................................ 12 GB baseline + Agents * 3 GB/month
  + Wazuh Indexer Indices ...................... Agents * 3 GB/month * Retention Months
  + Keycloak DB ................................ 0.07 GB (stable)
  + etcd ....................................... 0.15 GB (stable)
  + Buffer/Headroom ............................ 20% of total

9.2 Scaling Examples

Example A: Small Deployment (5 AWS Accounts, 2,500 Resources, No SecOps)

Disk = 45 + 0.8 + (5 * 0.01) + (2500 * 0.005 / 1024) + 0.04 + 0.05 + 0.2 + (5 * 0.5) + 5 + 0.07 + 0.15
     = 45 + 0.8 + 0.05 + 0.012 + 0.04 + 0.05 + 0.2 + 2.5 + 5 + 0.07 + 0.15
     = 53.9 GB * 1.2 (headroom)
     ≈ 65 GB → Recommend 100 GB

Example B: Medium Deployment (20 AWS Accounts, 10,000 Resources, SecOps with 5 Agents, 3 Months Retention)

Disk = 45 + 0.8 + (20 * 0.01) + (10000 * 0.005 / 1024) + 0.04 + 0.05 + 0.2 + (20 * 0.5) + 5 + 12 + (5 * 3 * 3) + 0.07 + 0.15
     = 45 + 0.8 + 0.2 + 0.05 + 0.04 + 0.05 + 0.2 + 10 + 5 + 12 + 45 + 0.07 + 0.15
     = 118.6 GB * 1.2
     ≈ 142 GB → Recommend 200 GB

Example C: Large Enterprise (50 Accounts, 50,000 Resources, SecOps with 20 Agents, 6 Months Retention)

Disk = 45 + 0.8 + (50 * 0.01) + (50000 * 0.005 / 1024) + 0.04 + 0.05 + 0.2 + (50 * 0.5) + 5 + 12 + (20 * 3 * 6) + 0.07 + 0.15
     = 45 + 0.8 + 0.5 + 0.24 + 0.04 + 0.05 + 0.2 + 25 + 5 + 12 + 360 + 0.07 + 0.15
     = 449 GB * 1.2
     ≈ 539 GB → Recommend 500 GB+ (SSD recommended for Wazuh I/O)

9.3 Host RAM Formula (Refined)

Total RAM (GB) =
    Host OS Overhead ........................... 2 GB
  + Core APIs (mcm-api + keycloak + apisix
    + etcd + postgres) ......................... 2.2 GB
  + MongoDB WiredTiger Cache ................... 2 GB minimum (auto-scales to 50% of available)
  + Elasticsearch JVM Heap ..................... 2 GB (1 GB configured + overhead)
  + Per Business Module (Gov, FinOps, Orch) .... 0.7 GB each
  + Discovery Module ........................... 0.8 GB idle + 0.7 GB per actively polled account
  + Wazuh Stack (if enabled) ................... 3.5 GB (manager 0.8 + indexer 1.5 + dashboard 1.0 + secops-api 0.8)
  + Observability (if enabled) ................. 1.7 GB (fleet 0.4 + kibana 0.7 + filebeat 0.1 + obs-api 0.6)

9.4 Host vCPU Formula (Refined)

Total vCPUs =
    Host OS & Docker ........................... 1 vCPU
  + Core APIs .................................. 1.5 vCPUs
  + Databases (MongoDB + ES) ................... 2 vCPUs
  + Per Business Module ........................ 0.25 vCPUs each
  + Discovery (active polling) ................. 0.5 + CEIL(concurrent_accounts / 2) vCPUs
  + Wazuh Stack (if enabled) ................... 2 + CEIL(EPS / 500) vCPUs
  + Observability (if enabled) ................. 1 vCPU

10. Tenant Lookup Issue — Engineering Note

Problem: "Unknown Tenant" in Telemetry Reports

The telemetry script's "Accounts Grouped by Tenant" and "In-Depth Disk Space Consumption per Tenant" sections show Unknown Tenant for all tenants. This occurs because:

  1. The accounts collection stores tenantId as a plain string (e.g., "6a391a1f44b04432c4ad7810").
  2. The enterprise collection uses enterpriseId as the lookup key, which may differ from the _id field.
  3. The $lookup join between accounts.tenantId and enterprise.enterpriseId fails silently when the field values don't match.

Root Cause: The enterpriseId field in the enterprise collection stores a different identifier format than what the accounts.tenantId references. This needs further investigation against the Java entity model (@Field annotations in mcm-api).

Impact: Cosmetic only — the tenant data sizes are still calculated correctly; only the display name resolution fails.


MCM VM Sizing Calculation Guide v1.0.0

© 2026 RevDau Industries Private Limited. All rights reserved.

This document is proprietary and confidential. Unauthorized distribution is strictly prohibited.

On this page