Skip to content
Stribog

Operations

All writing

Datadog, Splunk, Grafana, Elastic: What Leaving Costs

Datadog vs Splunk vs Grafana vs Elastic, judged on leaving: which agent you swap, which query language you rewrite, and what format your archived data is in.

Stribog15 min read

Most comparisons of these four vendors model a single hypothetical workload, multiply it by list prices and declare a cheaper winner. That answers the question a buyer asks in year one. The question that matters in year four is different: if the renewal goes badly, what does it cost to walk away? This article answers it from each vendor's own documentation — what their archive actually is, what their query language commits you to, and which meters run while you migrate — and then prices the self-hosted alternative honestly, people included.

The Bill Is Not What Holds You

Observability lock-in has three layers, and they have very different exit costs. The agent is what runs on every host and emits telemetry. The retained data is months or years of logs, metrics and traces that compliance, incident review or capacity planning may still need. The queries are the dashboards, saved searches and alert rules your on-call rotation depends on — written in a vendor-specific language and tuned against that vendor's data model.

A per-GB price comparison touches none of them. It prices the steady state of a system you have not yet left. The general method for turning lock-in into a number — inventory the artefacts, price their conversion, add the parallel run — is set out in the exit-cost modelling article. Telemetry deserves its own treatment because it is the data that accumulates fastest, is negotiated about least, and is hardest to take with you.

Four Meters Counting Four Different Things

The four vendors do not even bill the same unit, which is the first reason a single cheaper number does not exist. All figures below are list-price units from each vendor's public pricing page at the time of writing, before volume discounts. They are not workload totals and should not be multiplied into one.

  • Datadog prices Infrastructure Monitoring per host, per month in two tiers: Infrastructure Pro starts at $15 billed annually or $18 on demand, with 15-month metric retention; Enterprise starts at $23 billed annually or $27 on demand. Log Management meters ingest per GB of uncompressed data and rehydration per GB of compressed data scanned. Custom metrics are counted separately, and a custom metric is a unique combination of metric name and tag values, host tag included — so a tag is a billing decision.
  • Splunk offers activity-based, ingest, workload and entity pricing. On Splunk Cloud Platform, a workload subscription buys an entitlement in Splunk Virtual Compute (SVC), a unit of compute, memory and I/O, and does not meter ingestion. An ingest-based subscription is the other way round: it is licensed in GB/day, and SVCs are allocated from that at up to one SVC for every 10 GB/day.
  • Grafana Cloud Pro charges metrics from $6.50 per 1k active series, behind a $19 monthly platform fee that includes 10k series. Pro includes one data point per minute (DPM) per series, and billable usage is whichever is higher: active series, or total DPM divided by the included DPM — so a 30-second scrape doubles billable series usage for the same series, and a 15-second scrape quadruples it. Logs are priced per GB in three stages — process from $0.050, write from $0.400, retain from $0.100 — with 50 GB of ingest included.
  • Elastic Serverless Observability bills ingest of logs, traces and other signals from $0.07 per GB ingested and retention from $0.017 per GB retained per month. These are Serverless rates; Elastic Cloud Hosted is priced differently and is not covered here.

Notice what each meter rewards. Datadog's host and tag-combination units reward few hosts and disciplined tagging. Splunk's ingest tier rewards filtering before data arrives. Grafana's series-or-DPM unit punishes cardinality and short scrape intervals alike; Elastic's ingest-plus-retention split rewards short retention. A workload that is cheap on one meter can be expensive on another, which is why vendor-authored comparisons always seem to find their own product cheapest. The meter is also the first thing you must keep running during an exit, which matters later.

The Agent Is the Cheap Layer, Mostly

The agent used to be the deepest lock-in: a proprietary daemon, proprietary instrumentation libraries, a proprietary wire format. That has largely gone. The OTLP specification is stable for traces, metrics and logs, and the OpenTelemetry Collector is a vendor-agnostic way to receive, process and export all three. Every vendor here now meets it:

  • Datadog offers the DDOT Collector, its own open-source Collector distribution shipped inside the Agent (only Remote Configuration for it is in Preview), and the Datadog Agent itself ingests OTLP traces and metrics since 6.32.0/7.32.0 and logs since 6.48.0/7.48.0, over gRPC or HTTP.
  • Splunk ships a Distribution of the OpenTelemetry Collector — for Splunk Observability Cloud. Splunk Cloud Platform and Enterprise, where the log search lives, are still fed classically by the Universal Forwarder, which streams to a Splunk receiver. Do not assume one replaces the other.
  • Grafana describes Alloy as a vendor-neutral distribution of the OpenTelemetry Collector.
  • Elastic Agent includes a built-in, open-source Collector distribution, and Elastic Cloud offers a Managed OTLP Endpoint; for self-managed, ECE and ECK clusters, Elastic directs you to run Elastic Agent as the OTLP gateway.

Two caveats keep this from being a clean win. First, Datadog states plainly that data from applications instrumented with the OpenTelemetry SDK cannot be used in some proprietary products — App and API Protection, Continuous Profiler and Ingestion Rules among them. OTLP-first instrumentation keeps the core signals portable, but a team that relies on those products is still tied to Datadog's own libraries for them. Profiling is a separate case anyway: OTLP's profiles signal is still in development, which is why a vendor-free profiling stack takes a different route. Second, logs are a stable OTel signal but SDK maturity varies by language: Java and .NET are stable, Go is a release candidate, Python and JavaScript are still in development. For those services, a Collector tailing log files can be the safer path for now.

With OTLP at the edge, one Collector can feed the incumbent and the replacement at once. The configuration below sends every signal to the Datadog Agent's OTLP receiver — which must be enabled in `otlp_config`, with logs needing both logs_enabled: true and otlp_config.logs.enabled: true, because Datadog keeps OTLP logs off by default to avoid unexpected billing — and a second copy to self-hosted backends on their native OTLP paths: Mimir and Loki both take the otlphttp exporter pointed at /otlp, and Tempo accepts OTLP on its distributor.

yaml
# OpenTelemetry Collector: one OTLP intake, two destinations per signal.
# Hostnames and ports are examples; use your own services' addresses.
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  memory_limiter:
    check_interval: 1s
    limit_percentage: 80
    spike_limit_percentage: 20
  batch: {}

exporters:
  # Copy 1: the incumbent, via the Datadog Agent's own OTLP receiver.
  otlp/datadog-agent:
    endpoint: datadog-agent.observability.svc:4317
    tls:
      insecure: true # plaintext inside the cluster network only
  # Copy 2: the self-hosted backend, on each project's native OTLP path.
  otlphttp/mimir:
    endpoint: http://mimir-gateway.observability.svc:8080/otlp
  otlphttp/loki:
    endpoint: http://loki-gateway.observability.svc:3100/otlp
  # Tempo's OTLP receiver defaults to localhost:4317; bind it to 0.0.0.0.
  otlp/tempo:
    endpoint: tempo-distributor.observability.svc:4317
    tls:
      insecure: true

service:
  pipelines:
    metrics:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlp/datadog-agent, otlphttp/mimir]
    logs:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlp/datadog-agent, otlphttp/loki]
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlp/datadog-agent, otlp/tempo]
A dual-write Collector. Swapping the incumbent means changing one exporter, not re-instrumenting services. Use TLS anywhere telemetry crosses a trust boundary.

One subtlety the YAML cannot fix: a swapped agent emits different metric names and labels from the one it replaces. Dashboards keyed on the old names will not light up on the new backend until they are rewritten — which is the real cost, and the subject of the next two sections.

What You Are Holding on the Way Out

This is where the four vendors diverge most, and where ranking pages are silent. Each one's exit artefact is a different object with a different path back to searchable.

Five offerings, five different exit objects. Datadog's archive needs no particular engine to read; Grafana's needs open-source Loki; the rest need the vendor's own.

Datadog: compressed JSON you already own

Datadog Log Archives forward every ingested log — indexed or not — to a cloud storage bucket you own, as compressed JSON: zstd (.json.zst) by default, gzip optionally, under dt=<YYYYMMDD>/hour=<HH>/ prefixes. That is the most portable artefact of the four: open formats, your bucket, readable today with stock tools. The catch is the meter on the way back *in*: rehydrating into Datadog is billed per GB of compressed data scanned, plus the indexing rate for events that match the query, and Flex Logs retention reaches up to 15 months only while you stay.

bash
#!/usr/bin/env bash
# Read one hour of Datadog log archives with standard tools. No Datadog needed.
set -euo pipefail

BUCKET="s3://example-dd-archive/prod" # archive bucket and prefix
DAY="20261001"                        # dt=YYYYMMDD
HOUR="14"                             # hour=HH
OUT="./dd-archive/${DAY}-${HOUR}"

mkdir -p "$OUT"
aws s3 cp --recursive "${BUCKET}/dt=${DAY}/hour=${HOUR}/" "$OUT/"

for f in "$OUT"/*.json.zst "$OUT"/*.json.gz; do
  [ -e "$f" ] || continue
  case "$f" in
    *.zst) decompress=(zstd -dc) ;;
    *.gz) decompress=(gzip -dc) ;;
  esac
  # Survey the field set first: it reflects your pipelines and tag settings.
  "${decompress[@]}" "$f" \
    | jq -c 'if type == "array" then .[] else . end | keys' \
    | sort | uniq -c | sort -rn >"$f.fields"
  head -n 5 "$f.fields"
done
Survey before you convert: the fields in an archive depend on your own pipelines and whether tags are included.

Splunk: a journal, and a default that deletes

Splunk Cloud's Dynamic Data Self Storage (DDSS) moves expired data to your own S3 or GCS bucket, but only the rawdata journal survives the trip — the tsidx index files do not — and once it lands, Splunk keeps no copy and offers no management of it. Searching it again means re-indexing. Dynamic Data Active Archive, by contrast, keeps data in a Splunk-maintained archive, which is convenient and is the opposite of an exit path.

On Splunk Enterprise the trap is a default. In indexes.conf, `frozenTimePeriodInSecs` defaults to 188697600 seconds — six years — and without a coldToFrozenScript, data is deleted when it rolls to frozen. Splunk's archiving guide puts it bluntly: by default, the indexer deletes all frozen data. coldToFrozenDir is the alternative to a script:

ini
# indexes.conf (Splunk Enterprise). Frozen buckets are archived, not deleted.
[web]
homePath   = $SPLUNK_DB/web/db
coldPath   = $SPLUNK_DB/web/colddb
thawedPath = $SPLUNK_DB/web/thaweddb
# Default is 188697600 (6 years). Here: 7 years, as an example obligation.
frozenTimePeriodInSecs = 220752000
# Without this or a coldToFrozenScript, frozen data is deleted.
coldToFrozenDir = /mnt/splunk-frozen/web
Add the stanza to local/indexes.conf rather than replacing the file. Set retention from your obligation, and give frozen data somewhere to go.

Elastic: snapshots on Hosted, managed backups on Serverless

On Elastic Cloud Hosted and self-managed clusters, the exit artefact is a snapshot — Elastic says you must use the built-in snapshot functionality for cluster backups. Two properties matter. A snapshot cannot be restored to an earlier Elasticsearch version — and a newer one is not automatically safe either: Elastic's compatibility matrix keys on the version that created each index, so a 6.8 index does not restore to 7.0–7.1 or 8.0–8.2, and a 7.2–7.17 index restores to 9.x only as an archive index or a searchable snapshot. It also bundles stack internals — ILM policies, index templates, Kibana saved objects, alerting rules, Fleet settings. Elastic warns that Kibana saved objects and other feature data are not guaranteed valid across versions even when the index restore succeeds. That is a backup format, not an interchange format. Where a non-Elastic target such as OpenSearch fits, the licence and compatibility history is covered in the OpenSearch vs Elasticsearch article, and should be tested rather than assumed. Note also that searchable snapshots require an Enterprise licence, and the frozen tier holds only those. To export before leaving, register a repository you own — this is the self-managed body of PUT _snapshot/exit_repo, with credentials in the keystore, not the request:

json
{
  "type": "s3",
  "settings": {
    "bucket": "example-observability-exit",
    "base_path": "elastic/prod-2026-10",
    "client": "default"
  }
}
Self-managed: keystore keys s3.client.default.access_key and s3.client.default.secret_key. On Elastic Cloud Hosted use client secondary, store s3.client.secondary.access_key and s3.client.secondary.secret_key, and restart the cluster before verifying. Then PUT _snapshot/exit_repo/final?wait_for_completion=true.

On Elastic Cloud Serverless none of that applies. Elastic's comparison page states that Serverless backups are managed by Elastic and that user-initiated snapshots are anticipated in a future release. No documented bulk export for Serverless was found, so the remaining paths are re-exporting through the query APIs — metered as egress — or re-ingesting from the original source.

Grafana Cloud: portable logs, if you start early

Grafana Cloud's Cloud Logs Export syncs logs to a bucket you own in Loki's open-source chunk format, queryable with LogCLI or a self-hosted Loki — a genuinely portable artefact. The constraints are operational: one bucket only, in the same provider and region as your Logs cell, and syncing starts seven days after setup and covers logs from younger than 30 days up to 7 days old. The exit consequence is a timing rule: enable it long before cancelling. Anything already older than 30 days when you switch it on is outside its window, and the final seven days before cancellation have not been synced. For metrics, no documented bulk export was found; the Prometheus-compatible query APIs are the path, and a parallel remote write to your own Mimir only captures data from that day forward.

The Query Rewrite Nobody Budgets

Every dashboard panel and alert rule is code in a vendor dialect. SPL covers Splunk's search commands, functions, arguments and clauses. Datadog's metric syntax looks like avg:system.disk.free{*}.rollup(avg, 60). Elastic has two: KQL only filters and does not aggregate, transform or sort, while ES|QL is a piped language that does. On the open side, Mimir speaks PromQL and LogQL is based on PromQL, so one family covers metrics and logs. The same intent, illustrated across dialects:

text
# Intent: count 5xx responses per service. Illustrative; field names vary.

SPL      index=web status>=500 | stats count by service
ES|QL    FROM logs-* | WHERE http.response.status_code >= 500
           | STATS errors = COUNT(*) BY service.name
KQL      http.response.status_code >= 500        (filter only, no count)
LogQL    sum by (service) (count_over_time({env="prod"} | json | status >= 500 [5m]))

# Intent: free disk per host, one-minute average.

Datadog  avg:system.disk.free{*} by {host}.rollup(avg, 60)
PromQL   avg by (instance) (avg_over_time(node_filesystem_free_bytes[1m]))
Syntax is the easy part. The metric names, label keys and aggregation semantics differ too, and every alert threshold must be re-validated.

Visualisation is where an exit can be staged. A Kibana data view points only at Elasticsearch indices, data streams or aliases. Grafana, in contrast, ships an Elasticsearch data source preinstalled — so in the Kibana vs Grafana question, the practical answer for a team leaving Elastic is to rebuild dashboards in Grafana against the existing cluster first, then move the data underneath them. Grafana dashboards are the same artefact on Cloud and self-hosted; Grafana's Cloud Migration Assistant moves dashboards, folders, data sources and alerting from OSS or Enterprise into Cloud, which shows how much of the model is shared. Datadog dashboards can be retrieved through its Dashboards API, which gives you an inventory — but the queries inside still need translating.

Budget the rewrite by count, not by feeling: export the list of dashboards, saved searches and alert rules, classify each as trivial, semantic or unknown, and sample-convert a handful of each before committing to a date.

Dual-Running Without Tripping a Meter

No responsible exit cuts over cold. Both systems run in parallel until queries and alerts agree, and the parallel run has costs of its own:

The parallel run is when costs peak: two backends, two meters, and an egress bill if data crosses a region boundary.
  • Splunk licence violations block search. On Splunk Enterprise, exceeding the licensed daily volume raises a warning; for a stack under 100 GB/day, 45 warnings in a rolling 60-day period is a violation, and while in violation search — including scheduled reports and alerts, though not _internal — is blocked, though indexing continues. Stacks at 100 GB/day or more get warnings without the search block. A migration tends to add ingest: a new pipeline duplicating a source the forwarder already sends, a backfill to compare results. Losing alerting mid-migration is the worst possible moment. Filter at the Collector so the incumbent receives no more than it did before.
  • Datadog tags multiply. Tagging the copies to tell them apart creates new tag-value combinations, each a custom metric, and a DISTRIBUTION metric generates five custom metrics per combination by default. Label copies on the self-hosted side, never on the incumbent.
  • Egress has a price. Pulling data out of a managed service is metered; Elastic Serverless Observability lists 50 GB free per month, then $0.05 per GB. Price the historical re-export before you start it.

Pricing the Self-Hosted Side, Headcount Included

The honest comparison is not vendor invoice against storage cost. It is vendor invoice against storage, compute and the people who run it. What follows is a model, not a measurement — the inputs are yours, because salaries, volumes and retention obligations vary far too much for any published figure to be meaningful:

  1. Storage: GB ingested per day × retention days × compression ratio × object-storage price per GB-month. Measure your own compression on a day of real data.
  2. Compute: ingesters, queriers and compactors sized for peak query load, not average ingest — incident-time queries are the bursts.
  3. People: the fraction of engineers' time spent on upgrades, capacity and on-call for the observability stack itself, multiplied by your loaded cost. Be honest: someone must be paged when the system that pages people is down.
  4. Exit project (one-off): rewrite count from the previous section × hours per class, plus the parallel-run months at both bills.

Licences are part of the model too. Grafana Labs relicensed Grafana, Loki and Tempo to AGPLv3 in 2021, keeping plugins, agents and certain libraries Apache-licensed; Mimir is AGPLv3 and Alloy is Apache 2.0. AGPL's distinctive obligation concerns modified versions offered to users over a network, so unmodified internal use is the simple case — but have counsel confirm yours. Elastic, for its part, announced in August 2024 that it would add AGPL next to ELv2 and SSPL. The Prometheus Remote-Write specification standardises an existing, widely adopted protocol, which is exactly the property you want in the layer you plan to keep. A full build guide for the self-hosted stack is in the self-hosted observability article; cost attribution once it runs is covered in the OpenCost showback article.

Retention Is an Audit Obligation, Not a Setting

The vendor defaults differ wildly, and none of them is your obligation. Splunk Enterprise keeps six years and then deletes. Grafana Cloud Pro retains metrics for 13 months and logs, traces and profiles for 30 days. Datadog's pricing page lists 15-month metric retention on Infrastructure Pro and up to 15 months for Flex Logs. An auditor does not ask what the default was; they ask for the evidence.

The audit-grade test for any exit plan is simple: on the day after the contract ends, can you still produce every record your obligations require, in a format readable without the vendor, for the full required period? Datadog's JSON passes on format. A Splunk rawdata journal passes only if you keep a Splunk-compatible indexer to read it. An Elasticsearch snapshot passes only with a compatible Elasticsearch. For security-relevant logs specifically, the Kubernetes audit-log SIEM article covers retention and jurisdiction in a pipeline you operate.

Negotiate the Exit While You Still Have Leverage

Leverage is highest before signature and lowest at renewal, after years of telemetry have piled up in one format. Before signing any of these four, get three things in writing: the archive format your data leaves in, the export path for every signal — not just logs — and who owns retention when the contract ends. Then instrument OTLP-first, so the agent layer stays the cheap one. Ownership changes too: Cisco completed its acquisition of Splunk on 18 March 2024, and a ten-year telemetry strategy should assume more such changes, not fewer.

None of this argues that every team should self-host. It argues that every team should be *able* to leave — and that the ability is designed in month one or not at all.

§FAQ/Common questions

Frequently asked

Datadog vs Splunk: which is easier to leave?

On retained logs, Datadog is easier. Its Log Archives send every ingested log, indexed or not, to a bucket you own as compressed JSON (zstd by default, gzip optional), readable with standard tools. Splunk Cloud's Dynamic Data Self Storage keeps only the rawdata journal — not the tsidx index files — and Splunk keeps no copy after the move, so searching it again means re-indexing. On Splunk Enterprise, frozen data is deleted by default unless you set coldToFrozenDir or a coldToFrozenScript. Both still require rewriting queries and alerts: SPL on one side, Datadog's metric and log syntax on the other.

Does adopting OpenTelemetry remove observability vendor lock-in?

It removes most agent lock-in, not data or query lock-in. Datadog, Splunk, Grafana and Elastic all ship or accept an OpenTelemetry Collector, so one OTLP pipeline can feed any of them. But Datadog states that data from apps instrumented with the OpenTelemetry SDK cannot be used in some proprietary products, including App and API Protection, Continuous Profiler and Ingestion Rules, and OTel logs SDK maturity varies by language. Retained data and dashboards remain vendor-specific either way.

Can I export my data from Grafana Cloud or Elastic Cloud?

Partly. Grafana Cloud Logs Export syncs logs in Loki's open-source chunk format to one bucket you own, in the same provider and region, starting seven days after setup and covering logs between 7 and 30 days old, so enable it well before you leave. No documented bulk export for Grafana Cloud metrics was found; the query APIs are the path. On Elastic Cloud Hosted you can snapshot to your own repository; it cannot be restored to an earlier Elasticsearch version, the target must be compatible with each index's creation version (a 6.8 index does not restore to 8.0–8.2), and Kibana saved objects may not be valid across versions even when the restore succeeds. On Elastic Cloud Serverless, backups are managed by Elastic and user-initiated snapshots are not yet available.

Kibana vs Grafana: which should I use when leaving Elastic?

Kibana reads only Elasticsearch, through data views over indices, data streams or aliases. Grafana ships an Elasticsearch data source preinstalled, so a team leaving Elastic can rebuild dashboards in Grafana against the existing cluster first and move the data underneath later. KQL queries only filter, so aggregations live in visualisations or ES|QL and must be rewritten in PromQL or LogQL for a Prometheus or Loki backend.

datadog vs splunksplunk vs elasticsearchkibana vs grafanaobservability vendor exit costdatadog log archives rehydrationsplunk ddss rawdata journal

Executive Briefing

Thirty minutes to clarify your infrastructure risk

Walk us through your vendor footprint and regulatory constraints. We will tell you honestly where sovereignty creates leverage — and where it does not. No pitch deck. No obligation.