Prometheus & Grafana Requirements — Per Target Growth Model
Version 1.7.0 | Updated: October 06, 2026
Prometheus & Grafana Requirements — Per Target Growth Model
How to size the stack when adding servers and exporters.
Every number below is measured, on a named environment, on a stated date. Where a figure is an extrapolation rather than a measurement, it says so — the distinction matters, because the two deployments we run differ by more than 2× in total series and by 8× on individual exporters.
Versions this describes: Prometheus v3.13.1, Grafana 12.3.0, both pinned
in prometheus-grafana-stack/.env.example, which is the runtime truth.
1. The two reference deployments
Measured 05.08.2026.
On-prem (grafrd) — VMs and Compose, ~10 monitored hosts:
-
38 targets, 86.6k active series
-
Prometheus: 0.8 GB resident, 28.9 GB on disk, 10.7k samples/s
-
Retention 90 days, and the TSDB holds a full 90.1 days — so the disk figure is a steady state, not a database still filling up
-
Host: 12 vCPU, 62 GB RAM, 436 GB disk
Kubernetes (ocp-frdly, namespace acs) — 3 nodes:
-
36 targets, 220.6k active series
-
Prometheus: 1.2 GB resident, 6.9k samples/s
Scrape interval is 10s globally, with 5s on two jobs and 30s on several
others (prometheus.yml). Earlier guidance in this file said 60s, which
understates ingestion — and therefore disk — by about six times.
2. Series per target — measured, both environments
count by (job) ({name=~".+"}) divided by that job’s target count.
A dash means the job does not exist in that environment.
| Exporter / Job | Kubernetes | On-prem | Notes |
|---|---|---|---|
kube-state-metrics |
~26.3k |
— |
Cluster object inventory; scales with objects, not nodes |
cAdvisor |
~20.7k |
~0.78k |
Per-container. Dominates in Kubernetes, a rounding error on VMs — it scales with pods per node |
MySQL Exporter |
~18.2k |
~27.7k |
The largest single exporter on-prem. Grows with tables and |
JmxExporter |
~10.6k |
~2.6k |
Hazelcast + WildFly MBeans. Fixed set per member |
process-exporter |
~8.4k |
~2.6k |
Grows with matched process groups |
node-exporter |
~7.2k |
~3.2k |
Varies with filesystem and interface count |
Longhorn |
~2.8k |
— |
Only where Longhorn is the storage provider |
ClickHouse |
~1.9k |
~0.82k |
Native endpoint |
Prometheus (self) |
~1.3k |
~1.6k |
|
PostgreSQL |
~0.77k |
~0.70k |
|
Alertmanager |
~0.54k |
~0.48k |
|
Northbound API |
~0.53k |
~1.5k |
|
Provision API |
~0.48k |
~0.41k |
|
FTACS / acs-metrics |
~0.48k |
~0.19k |
|
ft-system-metrics |
~0.46k |
~0.76k |
|
UI Backend |
~0.39k |
~0.65k |
|
Service API |
~0.38k |
~0.32k |
|
FT Device Network |
~0.15k |
~0.18k |
|
AI Agent |
~0.06k |
~0.09k |
|
Nginx |
~0.06k |
~0.06k |
|
kubelet |
~0.01k |
— |
Three things to take from this.
Size for the infrastructure exporters, not the applications. In Kubernetes, kube-state-metrics plus cAdvisor, process-exporter and node-exporter are 64% of the total; add MySQL and Longhorn and it is 77%. On-prem, MySQL alone is a third. Meanwhile all nine application and API jobs together come to ~4.2k in Kubernetes and ~4.3k on-prem — under 2% either way.
The same exporter is not the same size in both. JmxExporter reports 4× more per target in Kubernetes, process-exporter 3× more, node-exporter 2× more — different hosts, different process lists, different filesystems. Use the column that matches where you are deploying.
These are orders of magnitude, not constants. A node exporter on a host with many filesystems reports considerably more than one with few.
Re-measure rather than trusting this table when it matters:
# on-prem
curl -sG --data-urlencode 'query=count by (job) ({__name__=~".+"})' \
http://localhost:9090/api/v1/query
# Kubernetes
kubectl -n <ns> exec deploy/ft-prometheus -- \
wget -qO- 'http://localhost:9090/api/v1/query?query=count+by+(job)+({__name__=~".%2B"})'
3. Storage
Derived from the on-prem measurement — 86.6k series, 10s scrape, a full 90 days on disk:
~0.33 GB per 1k active series.
Retention and scrape interval both move that linearly: halving the interval doubles it, and so does doubling retention. At the default 90 days and 10s:
| Active series | Disk at 90d / 10s |
|---|---|
100k |
~33 GB |
250k |
~83 GB |
500k |
~165 GB |
1M |
~330 GB |
2M |
~660 GB |
Leave headroom above these. Compaction needs room to write a new block before dropping the old one, and a full disk stops ingestion rather than slowing it.
4. Growth calculation
-
List the exporters the new node will run.
-
Take the series-per-target values from §2, from the right column. If a database exporter or cAdvisor is among them, expect it to dominate.
-
Sum them and add to the current total (
prometheus_tsdb_head_series). -
Check §5 for CPU and memory, §3 for disk.
5. Scaling thresholds
-
<2M active series → 4 vCPU / 16 GB is sufficient.
-
2—4M → 8 vCPU / 32 GB, SSD 1 TB.
-
4—8M → 16 vCPU / 64—96 GB, SSD 2—4 TB.
-
>8M → 32 vCPU / 96—124 GB, SSD 4—8 TB.
Rules of thumb, not measurements: the largest deployment measured here is 220k series, so every row but the first is extrapolation. What the measurements do support is that the first row is not tight — at 220k series Prometheus holds 1.2 GB resident, so 16 GB has a great deal of room before memory is the limit.
Grafana load is driven by users and dashboards rather than by series count.
6. Container memory limits
Every service in this stack runs with an explicit mem_limit. A container
without one is bounded only by the machine: a single leaking service can consume
all host memory and take every other service down with it, including Prometheus
itself. MemoryLimit=0 on an FT container is a defect, not a default.
The limit is a hard ceiling — the kernel OOM-kills the container on reaching it. That makes an over-tight limit worse than none at all: it converts a slow problem into an abrupt kill of a healthy service, usually under the load that made it grow. Size from what the service uses on your host, not from a round number.
Shipped defaults, and where each comes from:
| Service | Default | Basis |
|---|---|---|
|
4g |
Grows with retention and series count — see §3 and §5. At 220k series it holds 1.2 GB resident. |
|
1g |
Grows with dashboards and concurrent users, not with series. |
|
512m |
Scales with the number of containers on the host. |
|
256m |
Scales with the number of processes on the host. |
|
512m |
The Oracle instant client is heavier than the others. |
|
512m |
JVM processes: heap plus JVM overhead. |
|
256m |
Fixed footprint; measured at 28—49 MB steady state. |
|
128m |
Fixed footprint; measured at 7—29 MB steady state. |
The sidecars sit an order of magnitude below their limits by design. The headroom is there so that a burst does not kill a working service, and so that genuine growth becomes visible as a rising fraction of the limit before it becomes an outage.
Every limit is an environment variable. Raise one in .env — for example
PROMETHEUS_MEM_LIMIT=8g — rather than editing docker-compose.yml, which an
upgrade overwrites. Each .env.example lists the variables for its services.
Two limits deserve a second look after any expansion: PROMETHEUS_MEM_LIMIT
when servers are added, and CADVISOR_MEM_LIMIT on a host that runs many
containers.
Watching usage against the limit
docker stats shows current usage and the limit side by side. In Prometheus,
container_memory_rss / container_spec_memory_limit_bytes gives the fraction
used, and ContainerHighMemory alerts above 80% of it.
Note that the cAdvisor job drops most series for our own infra and exporter
containers to control cardinality — but container_memory_rss,
container_memory_working_set_bytes and container_spec_memory_limit_bytes are
deliberately exempt for them. Without that exemption the containers we ship
limits for would be the only ones nobody could watch approach those limits.
7. Examples
A monitored host with a database, on-prem: node-exporter ~3.2k
process-exporter ~2.6k + cAdvisor ~0.8k + MySQL ~27.7k = ~34k series, four
fifths of it the database exporter. About 11 GB of disk at 90d/10s. The same
host without a database is ~7k, about 2 GB.
A Kubernetes worker node: cAdvisor ~20.7k + process-exporter ~8.4k
node-exporter ~7.2k = ~36k series before a single application pod, plus
~0.1—2k for each pod scheduled on it. About 12 GB of disk. Note that
kube-state-metrics (~26k) is per cluster, not per node — it is paid once.
Both stay far inside the <2M baseline.
branch release/1.7.0 · commit 582e727b3700ec06c4c6e544e853886fb8d272b9 · page generated October 06, 2026