ObservabilityLXVII · High AvailabilityHA
Two Instances Is Not HA
What you'll learn
- Define the parallel-silo pattern: two independent processes with no shared state and no replication
- Explain why running a second Prometheus, a second Loki, or a second Tempo does not provide HA
- Identify the silent-data-loss window created by parallel-silo deployments and the dashboards that fail to surface it
- Replace parallel-silo designs with the correct mechanism (Mimir/Thanos for Prometheus, microservices mode with replication_factor for Loki and Tempo)
Prerequisites
Verified against Prometheus 2.55.x · Alertmanager 0.28.x · node_exporter 1.8.x · blackbox_exporter 0.26.x · Grafana 11.x · Loki 3.x · Tempo current · OpenTelemetry Collector 0.110.x · Grafana Alloy current · Docker Engine 28.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-13
A Grafana dashboard says “Grafana HA enabled, 2/2 instances
healthy.” The two instances are Prometheus 2.55.x running in
single-binary mode on two different VMs, each scraping the same
targets. Both report up == 1. The dashboard is green. Six
months later a host failure takes down Prometheus A. Prometheus
B is still running and still reports up == 1 for the same
targets. The user-visible behaviour is: alerts continue to fire,
dashboards continue to render. But the metrics for the last
six months on Prometheus A are gone, because Prometheus B never
had them. Prometheus A and Prometheus B are two independent TSDBs
with two independent WALs and two divergent time series. The
“high availability” is an illusion made up by a panel that
checks processes, not data.
This is the most expensive HA mistake in the observability stack. It is also the most common.
What “two instances” looks like
The shape is simple and almost always accidental:
+-- VM A -- prometheus-2.55 -- TSDB A -- scrape targets
^
+-- VM B -- prometheus-2.55 -- TSDB B -- scrape targets
Variations:
- Two Loki single-binary processes, each with its own chunks directory, each receiving the same log stream from two Promtail clients (one configured to send to A, one to B).
- Two Tempo processes, each with its own WAL and trace storage, each receiving from two OTel Collectors.
- Two Mimir ingesters started with
replication_factor: 1and a non-shared ring (the “two ingester” anti-pattern). - A “primary/standby” pair with no automatic failover, no shared WAL, and a heartbeat that pages someone to “fail over” by hand.
The defining characteristic is no shared state. Each process is a complete silo. The data on process A does not exist on process B and never has.
Why this pattern appears
It appears because:
- It looks like HA. Two processes are running. Two processes are healthy. Two failure domains exist. The manager asks “is this HA?” and the engineer says “yes, two boxes.”
- It is the path of least resistance. No KV store is required. No replication factor is required. No Thanos or Mimir is required. Just run the binary twice.
- The failure mode is silent. When one box dies, the other keeps running. Nothing errors. Nothing alerts. The data on the dead box is gone, but the dashboards that should show “data is missing” are themselves driven by data that is missing.
What the silent data loss actually looks like
Take the two-Prometheus example. Both scrapes hit the same targets. Each receives its own copy of every sample. Each builds its own TSDB. The two TSDBs are independent.
When VM A dies:
Before After
+---------------------+ +---------------------+
| Prometheus A (live)| | Prometheus A (DEAD)|
| Prometheus B (live)| | Prometheus B (live)|
+---------------------+ +---------------------+
Both have full B has its own copy only.
6-month history. A's 6-month history is
gone. The "HA" survives.
The data does not.
The recovery path is:
- Replace VM A.
- Reinstall Prometheus on the new VM.
- Start scraping.
- Watch the new TSDB rebuild itself from now forward. The last 6 months are gone forever.
The alert that should have fired (“TSDB retention gap detected”) never existed. The panels that show data continuity never existed. The recovery procedure was a manual rebuild, not a failover, because there was nothing to fail over to.
The same failure shape in Loki and Tempo
Loki single-binary
Loki in mode: simple is also a single-binary TSDB. Two
“simple” mode Loki processes receiving the same log stream do
not replicate the stream. Each process has its own chunks
directory, its own index, and its own retention window. When
one dies, the data on it is gone.
The correct replacement is Loki in mode: microservices with
ingester.lifecycler.ring.replication_factor: 3. Then the
ingester ring handles replication and the two-process
anti-pattern is not the default.
Tempo
Tempo’s local trace storage (backend: local) is the same
shape. Two Tempo processes with backend: local are two
parallel silos. The replacement is backend: s3 (or GCS,
Azure Blob) plus distributor.receivers.otlp.replication_factor: 3 on the ingester ring. The traces live in shared object
storage; the ingesters are coordinated by a ring.
Grafana
Grafana 11.x is the exception that proves the rule. Two Grafana replicas pointing at the same external database are HA, because the data (dashboards, users, data sources) lives in the database, not in the Grafana process. The “two instances” pattern is correct only when the state is external to both instances.
How to detect the anti-pattern
Five checks confirm whether a “two instance” deployment is real HA or a parallel silo:
# READ-ONLY
# 1. Are both instances writing to the same data store?
# Prometheus:
curl -s http://prom-a:9090/api/v1/status/runtimeinfo | \
jq '.data.storageRetention'
# Loki (boltdb-shipper):
ls -la /loki/chunks/ /loki/index/
# If A and B have separate directories, the data is not shared.
# READ-ONLY
# 2. Is there a shared object store?
# Loki / Tempo / Mimir / Thanos all converge on this.
kubectl get configmap loki-config -o yaml | grep -E 's3|gcs|azure'
# If the answer is "local" or "filesystem," the system is
# using local-disk state and the two-instance pattern does
# not provide HA.
# READ-ONLY
# 3. Does the ring show the expected replication factor?
curl -s http://loki-distributor:3100/ring | \
jq '.shards[].replication_factor' | sort -u
# expected: a single value matching the configured R
# READ-ONLY
# 4. Is there a leader-election backend for singletons?
# (compactor, ruler)
kubectl get pods -l app=loki-compactor -o name | wc -l
# expected: 1 (active) + 1 (standby). More than 1 active is
# split brain. Zero means the compactor is down.
# READ-ONLY
# 5. Do the dashboards distinguish "process is up" from
# "data is intact"? If a panel exists that says "data continuity
# OK," the system is HA. If not, it is two processes.
Replacing the anti-pattern
The correct replacement depends on the stack:
| Current shape | Correct replacement |
|---|---|
| Two Prometheus single-binary | Thanos sidecar + object store, or Mimir ingest |
| Two Loki simple-mode | Loki microservices mode with R=3 + Consul/etcd |
| Two Tempo local | Tempo microservices + S3 backend + R=3 |
| Two Mimir monolith | Mimir microservices (already R=3 by default) |
| Two Grafana (different DBs) | Two Grafana sharing one external DB |
| Two Alertmanager (no gossip) | Alertmanager cluster with peers: and gossip ring |
The replacement is more complex than “run two boxes.” The complexity is the price of real HA. The benefit is that when one box dies, the surviving system already has the data and the recovery is automatic.
Production guidance
Verification
You should now be able to answer:
- What is the parallel-silo anti-pattern?
- Why does running two Prometheus processes not provide HA?
- What is the silent data loss window in a parallel-silo Loki or Tempo deployment?
- What is the correct replacement for each of the four parallel-silo shapes?
Quiz
Knowledge check · 8 questions
Q1. Two Prometheus 2.55.x single-binary instances scraping the same targets are running on two VMs. What kind of HA does this provide?
Q2. Which of these is the correct replacement for the two-Prometheus anti-pattern?
Q3. Which of the following are parallel-silo anti-patterns? (Select all that apply.)
Q4. A two-Prometheus deployment can replicate samples over the Prometheus HTTP query API.
Q5. When one of two parallel-silo Loki processes dies, what is the recovery procedure?
Q6. Name three checks that distinguish parallel-silo deployments from real HA.
Q7. A panel on the operator dashboard shows "Grafana HA: 2/2 instances healthy." This proves:
Q8. Two Grafana 11.x replicas pointing at the same MySQL database are real HA.
Passing score: 75%. Answers are checked in this browser.