Replace Splunk search with OpenSearch you own
Move operational search, dashboards, alerts, and indexed workflows from Splunk to an OpenSearch-based stack — without losing the saved searches your team runs every day.
Target architecture
OTel Collector → Data Prepper → OpenSearch → Object Storage

Scope
Every search workflow your team depends on, translated
Saved Searches
SPL queries analyzed, translated to OpenSearch DSL, and validated against production data.
Dashboards
Splunk dashboards recreated in OpenSearch Dashboards with equivalent visualizations and panels.
Indexes
Index configurations, field extractions, and data models mapped to OpenSearch index patterns.
Alerts
Scheduled searches and alert actions translated to OpenSearch alerting with equivalent triggers.
Outcomes
What you get from the Splunk Off-Ramp
Complete search inventory with usage analysis
Every saved search cataloged with execution frequency, owner, and criticality. Know exactly what matters before you translate anything.
High-value query translation with validation
Critical SPL queries translated to OpenSearch DSL with automated result comparison against production data.
Operational continuity through mirror mode
On-call teams keep their search patterns. Runbooks are updated. Alert coverage is maintained throughout the transition.
Semantic search and grounded copilot
OpenSearch enables vector search and semantic queries. The ExitGraph copilot adds AI-powered investigation grounded in your data.
Architecture
Target-state stack
OpenTelemetry Collector
Replaces Splunk Universal Forwarders with vendor-neutral, standards-based collection.
Data Prepper
OpenSearch-native data processing pipeline for log transformation, enrichment, and routing.
OpenSearch
Full-text search, analytics, and dashboarding. Drop-in replacement for Splunk search workflows.
S3-compatible Storage
ISM-managed lifecycle policies for cost-effective long-term retention with query-on-demand.
Translation
SPL to OpenSearch DSL — what translation looks like
index=web sourcetype=access_combined status>=500 | stats count by host, status | where count > 100 | sort -count
GET web-access-*/_search
{
"query": { "range": { "status": { "gte": 500 } } },
"aggs": {
"by_host": {
"terms": { "field": "host.keyword" },
"aggs": {
"by_status": {
"terms": { "field": "status" }
}
}
}
}
}