ObservabilityLXIX · Long-Term Metrics StorageLongTermStorage
Cortex Overview
What you'll learn
- Describe the Cortex architecture and its relationship to Mimir
- Explain why Cortex was archived by the CNCF and what that implies operationally
- Identify the migration path from Cortex to Mimir and the wire-protocol compatibility
- Configure a minimal Cortex deployment for legacy reference
- Recognise the failure modes unique to running unmaintained software in production
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 team inherits a Cortex cluster from a previous platform group. The docs say “Cortex 1.10”. The Cortex project was archived by the CNCF in 2024; the active fork, Grafana Mimir, ships security patches monthly. The migration path is documented and tested, but the team has spent six months deciding whether the project is “really dead” before starting.
Cortex is the project that Mimir came from. Many of the operational lessons in Mimir’s documentation come straight from Cortex. Some shops still run Cortex; most should not, and the reasons are specific.
What it is
Cortex is the original horizontally scalable, multi-tenant time- series database, originally built by Weaveworks in 2018, donated to the CNCF, and graduated in 2021. The architecture — Distributor, Ingester, Querier, Query-frontend, Compactor, Ruler — is the architecture Mimir inherited. Grafana Labs forked Cortex in 2022 into Mimir; the CNCF archived Cortex in 2024.
Cortex was the first TSDB to make the trade-off explicit: ingest scaling is bounded by sharding the head across Ingesters; query scaling is bounded by sharding blocks across Store gateways and parallelising reads through a Query-frontend. Mimir takes the same trade-off and adds the operational refinements (better defaults, more caching, better limits engine, query-splitting heuristics) that the Cortex project did not.
Why a sysadmin cares
There are two reasons to know Cortex in 2026. One is legacy inheritance: a team that joins an organisation running Cortex has to understand it to operate it, then plan the migration. The other is historical literacy: many of the Mimir concepts (hash-ring, sharding, query-frontend) come from Cortex, and the Cortex docs remain the most thorough references for those concepts.
The reason most teams should not be running Cortex is the project status. The CNCF archived Cortex; Grafana Mimir is the active fork and receives security patches monthly. New CVEs in Cortex have no upstream patches. New Prometheus-side features (native histograms, OTLP, new label matchers) may not be supported by older Cortex releases, and a stale Cortex cannot pick them up.
How it works
Cortex and Mimir are architecturally the same project at the level this course teaches:
Cortex architecture
-------------------
Prometheus -> Distributor -> Ingester -> Object store
remote_write (hash on (TSDB head,
series) per-tenant)
|
v
Query-frontend -> Querier -> Store gateway
(split, cache, (merge) (blocks from
parallelise) bucket)
The component names match Mimir; the configuration keys differ
(cortex_ prefix on metrics, cortex-distributor on flags, etc.).
The wire protocols are compatible: a Cortex deployment can be
swapped to Mimir by replacing the binaries and migrating the
configuration via the mimir config converter
(mimirtool config convert cortex.yaml). The data does not move;
the bucket stays the same.
How to configure it
A minimal Cortex configuration (/etc/cortex/config.yaml):
# Storage
storage:
backend: s3
s3:
bucket_name: cortex-blocks-prod
endpoint: s3.eu-west-1.amazonaws.com
region: eu-west-1
access_key_id: "${AWS_ACCESS_KEY_ID}"
secret_access_key: "${AWS_SECRET_ACCESS_KEY}"
# Distributor
distributor:
pool:
health_check_ingesters: true
remote_timeout: 10s
# Ingester
ingester:
ring:
replication_factor: 3
kvstore:
store: consul
lifecycler:
ring_kvstore:
store: consul
min_ready_duration: 1m
final_sleep: 1m
blocks_storage_enabled: true # modern; not the chunks backend
tsdb:
dir: /var/lib/cortex/tsdb
# Querier
querier:
query_timeout: 2m
max_concurrent: 200
# Query-frontend
frontend:
results_cache:
backend: memcached
memcached:
addresses: memcached-exporter.cortex.svc:11211
# Store gateway
store_gateway:
sharding_ring:
replication_factor: 3
The keys that mark a Cortex config as “1.10 era”:
# Chunks-era (deprecated, not supported in modern Cortex)
ingester.chunks_store_config: # legacy
storage.chunk_store_config: # legacy
# Blocks-era (modern; same as Mimir defaults)
ingester.blocks_storage_enabled: true
ingester.tsdb.dir: /var/lib/cortex/tsdb
If the config has chunks_store_config or chunk_store_config,
the deployment is older than the current Cortex blocks-storage
mode. The migration to blocks-mode (still on Cortex) is a separate
project; the migration from Cortex to Mimir can land afterwards.
How to validate it
# Cortex is up
curl -s cortex-frontend:8080/ready
curl -s cortex-frontend:8080/api/v1/status/config | jq '.status'
# A real query that proves the full path
curl -sG cortex-frontend:8080/api/v1/query \
-H 'X-Scope-OrgID: tenant-42' \
--data-urlencode 'query=count(up{job="prometheus"})'
# cortextool: the Cortex counterpart of mimirtool
cortextool check config /etc/cortex/config.yaml
# List blocks in the bucket (same as Mimir; the bucket is shared)
aws s3 ls s3://cortex-blocks-prod/ --recursive | head
The metrics that confirm Cortex is healthy use the cortex_
prefix:
# Distributor is accepting
rate(cortex_distributor_samples_in_total[1m])
# Ingesters are healthy
count(up{job="cortex-ingester"})
# Compactor is alive (1 instance per tenant group)
count(up{job="cortex-compactor"})
# No rejected samples
rate(cortex_distributor_samples_rejected_total[5m])
How it can fail
The failure modes of Cortex are the same as Mimir at the component level. The failure modes of running Cortex in 2026 are different, and they are the reason most teams should migrate:
- No upstream security patches. Symptom: a CVE is disclosed for an upstream dependency (gRPC, memberlist, AWS SDK); Cortex does not receive a patch; the cluster is exposed until the operations team backports. The risk grows over time as more CVEs accumulate.
- Cortex config key drift. Symptom: a Cortex operator
upgrade requires manual config changes; newer Cortex
releases deprecate keys the deployment still uses; the
config converter (
cortextoolormimirtool) flags the drift. Older keys may be silently ignored. - Chunks-mode configuration in a deployment that wants blocks-mode. Symptom: the bucket fills with chunk directories, not block ULIDs; the Compactor is misconfigured or absent. The migration from chunks to blocks is a separate project from the migration to Mimir.
- No native histogram support. Symptom: Prometheus 2.55
ships native histograms by default; a Cortex that does not
understand them rejects the batches at the Distributor.
Symptom in metrics:
cortex_distributor_samples_rejected_ totalclimbing for the affected tenants. - Authn header drift. Symptom: the
X-Scope-OrgIDheader is mis-set on a Prometheus side; tenant data crosses. Same root cause as Mimir, same fix: the tenant ID is a deployment- time variable, not a hand-edited string. - No upstream help when the bucket is empty. Symptom: a long outage, a misconfigured Compactor, a corrupted bucket, and no active maintainers to escalate to. The fix is to rebuild the data path or migrate; the operational relief of an upstream is gone.
How to troubleshoot it
The diagnostic discipline is identical to Mimir. The specific differences are:
- Is it Cortex or Mimir? Check the binary version
(
cortex -versionvsmimir -version). Mimir reports its version; Cortex does too. Mixing the two in one cluster is the first thing to rule out. - Is it chunks or blocks?
ls /var/lib/cortex/tsdb— block ULIDs (matching01[0-9A-Z]{24}) indicate blocks- mode; chunk directories indicate chunks-mode. The two are not compatible. - Is the config still valid?
cortextool check configormimirtool check config /etc/cortex/config.yaml. Themimirtoolaccepts Cortex YAML for the converter; a config it flags as not-convertible is a sign of severe drift. - Are samples being accepted? Same as Mimir:
cortex_distributor_samples_in_totalvs Prometheusremote_writesend rate. - Logs. Same structured logs as Mimir;
caller,tenant,component.
Security implications
- CVE exposure. The operational reason to migrate. A project that no longer ships patches is a project that accumulates CVEs. The CVE list for Cortex is published; a deployment running Cortex 1.10 in 2026 has every CVE from the gap year on it.
- Tenant authentication. Same as Mimir: every request
carries
X-Scope-OrgID; the authn proxy must set it correctly. The cross-tenant bug is the worst case; the mitigation is identical. - Bucket credentials. Same as Mimir and Thanos: prefer IAM
roles; restrict
DeleteObjectto the Compactor; rotate access keys. - gRPC mTLS. Same as Mimir: enable mTLS between Distributor, Ingester, Querier, and Store gateway. The HA attack surface is the same.
Performance implications
Cortex and Mimir have nearly identical performance profiles. The differences are small and specific:
- Mimir has better defaults for the Query-frontend. The query splitting heuristics and the cache TTL defaults are tuned in Mimir; Cortex users typically tune them by hand.
- Mimir has a more capable limits engine. Per-tenant policies in Mimir are richer than Cortex’s; teams that have outgrown Cortex’s limits often migrate for this alone.
- Cortex has slightly lower memory overhead on the Distributor. A historical artefact of the code; Mimir has since matched it. Not a meaningful reason to stay on Cortex.
Production sizing is the same. The migration cost is operational, not technical.
Verification
You should now be able to answer:
- What is Cortex, and what is its relationship to Mimir?
- Why was Cortex archived, and what does that imply for a team running it?
- Which configuration key marks a Cortex deployment as chunks-mode rather than blocks-mode?
- What is the migration path from Cortex to Mimir, and what moves and what does not?
- Which Prometheus-side feature does an old Cortex reject that Prometheus 2.55 ships by default?
Quiz
Knowledge check · 8 questions
Q1. What is the relationship between Cortex and Grafana Mimir?
Q2. The CNCF still ships security patches for Cortex in 2026.
Q3. Which statements are true about a Cortex-to-Mimir migration?
Q4. A Cortex config contains ingester.chunks_store_config and storage.chunk_store_config. What is the operational implication?
Q5. Name the metric prefix used by Cortex for Distributor sample counters.
Q6. A Prometheus 2.55 sending native histogram samples to an older Cortex will have them accepted by the Distributor.
Q7. Which is the supported way to migrate a Cortex YAML config to Mimir?
Q8. A team inherits a Cortex 1.10 deployment. What is the most operationally honest framing for 2026?
Passing score: 75%. Answers are checked in this browser.