Skip to main content

Migrating from Splunk

LynxDB speaks LynxFlow, a pipeline query language in the same tradition as Splunk's SPL. The pipe-stage model and most stage names (stats, sort, dedup, top) carry over directly, so most SPL knowledge transfers. This guide covers the key differences and provides a step-by-step migration path.

SPL2 history

LynxDB originally shipped an SPL2 dialect; it was replaced by LynxFlow v2 as a clean-break redesign. If you are migrating from an older LynxDB version rather than from Splunk, see the full capability mapping in docs/grammar/RFC-002.md §15 and the CHANGELOG section "Breaking Changes (LynxFlow vs SPL2)".

SPL vs LynxFlow Differences

Index Selection

# Splunk SPL
index=main sourcetype=nginx

# LynxDB LynxFlow
from main sourcetype=nginx

The from stage accepts search sugar -- bare terms, quoted phrases, and field=value comparisons -- so Splunk-style search expressions mostly carry over after replacing index=main with from main:

from main _source=nginx status>=500 | stats count() by uri

The Big Four Differences

  1. == compares, = binds: In expressions, write where status == 500, not where status = 500. The from-stage search sugar (level=error) is the single exception.
  2. count() requires parentheses: stats count() by host, not stats count by host.
  3. Renamed stages: eval is now extend, table/fields are now keep/drop, rex is now parse regex, and timechart is now every ... stats.
  4. search is from-stage only: Mid-pipeline, use where has(_raw, "term") or where contains(_raw, "substring") instead of | search term.

Quick Syntax Reference

OperationSplunk SPLLynxDB LynxFlow
Select indexindex=mainfrom main
Searchsearch errorfrom main error
Filterwhere status>500where status > 500
Aggregatestats count by hoststats count() by host
Time charttimechart count span=5mevery 5m stats count()
Time chart split by fieldtimechart count by host span=5mevery 5m by host stats count()
Top valuestop limit=10 uritop 10 uri
Field extractionrex field=_raw "(?<ip>\d+\.\d+\.\d+\.\d+)"parse regex r"(?P<ip>\d+\.\d+\.\d+\.\d+)"
Select columnstable _time, level, messagekeep _time, level, message
Compute fieldseval duration_sec=duration_ms/1000extend duration_sec = duration_ms / 1000
Fill nullsfillnull value=0 statusextend status = status ?? 0
Renamerename src AS source_iprename src as source_ip
Dedupdedup hostdedup host
Subsearch / CTE[search index=threats | fields ip]let $threats = from threats | keep ip;
Macro`my_macro`Not supported

Stage Mapping

Stages that keep their Splunk names: stats, sort, head, tail, dedup, rename, top, rare, streamstats, eventstats, join, union, transaction, where.

Stages that were renamed:

Splunk SPLLynxFlow
evalextend
table, fieldskeep, drop
rexparse regex (plus parse json, parse logfmt)
timechartevery <span> stats ... or stats ... by bin(_time, <span>)
binbin(_time, <span>) as a grouping function
fillnullextend f = f ?? value
search (mid-pipeline)where has(...) / where contains(...)

Aggregation Functions

All common Splunk aggregation functions are supported (note that count requires parentheses):

count(), sum, avg, min, max, dc (distinct count), estdc, values, list, stdev, var, mode, first, last, earliest, latest, perc(x, p), p50, p75, p90, p95, p99

Conditional aggregation replaces eval-wrapped tricks: stats count(where status >= 500).

Diagnostics

The parser reports errors with stable codes, caret spans, and suggestions, which catches most Splunk-isms immediately:

$ lynxdb query 'from main | stats count by host'
error[E013] at 1:19: count requires parentheses: count()
from main | stats count by host
^~~~~
suggestion: count()

Migration Steps

Step 1: Set Up LynxDB

# Install
curl -fsSL https://lynxdb.org/install.sh | sh

# Start server
lynxdb server --data-dir /var/lib/lynxdb

Step 2: Forward New Data via HEC

LynxDB includes a Splunk HTTP Event Collector (HEC) compatible endpoint. Point your existing Splunk forwarders at LynxDB with minimal configuration changes.

Universal Forwarder (outputs.conf):

# outputs.conf
[httpout]
httpEventCollectorToken = your-lynxdb-token
uri = https://lynxdb.company.com/api/v1/ingest/hec

[httpout:lynxdb]
uri = https://lynxdb.company.com/api/v1/ingest/hec
token = your-lynxdb-token

Heavy Forwarder (outputs.conf):

[tcpout]
disabled = true

[httpout]
disabled = false

[httpout:lynxdb]
uri = https://lynxdb.company.com/api/v1/ingest/hec
token = your-lynxdb-token

Step 3: Export Historical Data from Splunk

Export data from Splunk for import into LynxDB:

# Export from Splunk as CSV
splunk search 'index=main earliest=-30d' -output csv > splunk_export.csv

# Or export as JSON
splunk search 'index=main earliest=-7d' -output json > splunk_export.json

Import into LynxDB:

# Import CSV export
lynxdb import splunk_export.csv --source splunk-migration

# Import JSON export
lynxdb import splunk_export.json --format ndjson

# Validate before importing
lynxdb import splunk_export.csv --dry-run

# Import into a dedicated index
lynxdb import splunk_export.csv --index splunk

Step 4: Convert Saved Searches

Convert your Splunk saved searches to LynxDB saved queries:

# Splunk: index=main sourcetype=nginx status>=500 | stats count by uri | sort -count | head 10
# LynxDB:
lynxdb save "5xx-by-uri" 'from main _source=nginx status>=500 | stats count() by uri | sort -count | head 10'

# Run saved query
lynxdb run 5xx-by-uri --since 24h

Cost Comparison

Splunk EnterpriseLynxDB
License$2,000+/GB/day ingestedFree (Apache 2.0)
Infrastructure6+ components (indexer, search head, deployer, license server, etc.)Single binary
Memory~8GB minimum per component~50MB idle
ScalingComplex deployment server setupConfig flag change

Feature Comparison

FeatureSplunkLynxDB
Query languageSPLLynxFlow (SPL-inspired pipeline language)
Full-text searchtsidxFST + roaring bitmaps
SchemaOn-readOn-read
Materialized viewsData model accelerationMaterialized views (~400x)
Pipe modeNoYes
REST APIYesYes (streaming-first)
HEC compatibilityNativeCompatible endpoint

Next Steps