OpenSearch and OpenSearch Dashboards: compare, choose, migrate
Karim re-reads Elastic's terms of use and challenges Inès in the product meeting: "can we switch to OpenSearch to avoid licensing surprises when Veille goes SaaS?". The team spends a morning replaying the course requests on AWS's fork, comparing the two engines honestly and making the call. This module traces the boundary between what is identical, what diverges, and what really matters for a product that sells search.
Where the fork comes from
The tipping point is January 2021. Until then, Elasticsearch and Kibana were released under the Apache 2.0 license, a permissive license that lets anyone, including a cloud host, resell the software. Elastic announces the switch to a dual SSPL / ELv2 (Server Side Public License and Elastic License 2.0) starting with version 7.11. The message is explicit: AWS can no longer offer its managed service under the "Elasticsearch" name without negotiating a commercial agreement. SSPL, derived from AGPL, requires sharing the entire code used to offer the service as SaaS under the same license; ELv2 forbids providing a managed service.
February 2021, AWS responds with the fork. The starting point is the last Apache 2.0 release: Elasticsearch 7.10.2 and Kibana 7.10.2. The fork is renamed OpenSearch and OpenSearch Dashboards, developed in the open, originally governed by AWS. Version 1.0 ships in the summer of 2021.
Since then, two notable moves. In 2024, Elastic adds AGPLv3 as a third option for Elasticsearch: users can pick SSPL, ELv2 or AGPLv3. This is a partial return to the classic open source ecosystem — but the SaaS question remains. The same year, OpenSearch leaves AWS's exclusive orbit and joins the Linux Foundation inside a new foundation, the OpenSearch Software Foundation, where Uber, SAP, Aiven, Bytedance and others also sit. Governance becomes genuinely multi-stakeholder.
The whole vocabulary of the course (index, shard, mapping, text vs keyword, aggregations, Query DSL) stays true on both sides. Divergences live above: administration, security, commercial plugins, recent query languages.
Comparison table
The table below tracks version 9.5.3 of Elasticsearch shipped with the kit and version 3.8.0 of OpenSearch, at the time of writing.
| Criterion | Elasticsearch 9.5 | OpenSearch 3.8 |
|---|---|---|
| License | SSPL, ELv2 or AGPLv3 (optional) | Apache 2.0 |
| Governance | Elastic N.V. | OpenSearch Software Foundation (Linux Foundation) |
| Security (auth, RBAC, TLS) | Included in the free basic tier | security plugin included, free |
| Web console | Kibana | OpenSearch Dashboards |
| SQL / PPL | Basic SQL; ES|QL since 8.11 | sql plugin: SQL and PPL, free |
| Vector search | dense_vector, native kNN, ELSER, semantic_text | k-NN plugin, Neural Search |
| Alerting | Basic + paid tiers | Alerting plugin included, free |
| Index lifecycle | ILM | ISM (Index State Management), plugin |
| Official clients | elasticsearch v8 / v9 | opensearch-py, forks for JS, Java, Go |
| Cross-client compatibility | v8+ client not compatible with OpenSearch | OpenSearch client not recommended for Elastic |
| Managed hosts | Elastic Cloud, Bonsai, Aiven | AWS OpenSearch Service, Aiven, Bonsai |
Two rows deserve digging into. Included security: in Elasticsearch, TLS, users and roles have been in the free basic license since 2020, which many people still ignore. The veille-es kit uses it. On the OpenSearch side, the security plugin is natively integrated (but disabled in the kit's comparative profile to keep URL reading simple). Pipe languages: ES|QL on the Elastic side, PPL on the OpenSearch side are two distinct answers to the same need — replacing long JSON pipelines of Query DSL with a linear, readable syntax.
Elasticsearch (ES|QL)
FROM news | WHERE category == "POLITICS" | STATS c = COUNT() BY category | SORT c DESC
OpenSearch (PPL)
source=news | where category="POLITICS" | stats count() by category | sort -count()
Start OpenSearch in the kit
The kit ships a dedicated profile that coexists with Elasticsearch by shifting ports (9201 and 5602). Security is disabled on purpose in this profile so the comparative module stays readable; in production, we would enable the security plugin and TLS.
./lab.sh opensearch-up
Expected at the end:
[OK] OpenSearch ready on http://localhost:9201 — Dashboards on http://localhost:5602
Quick check without authentication:
GET _cluster/health
Expected response:
{
"cluster_name": "veille-os",
"status": "green",
"number_of_nodes": 1,
"active_primary_shards": "(your number may differ)"
}
Replay three course requests
The News corpus lives in Elasticsearch, not in OpenSearch — that is the whole point. To compare the Query DSL without re-importing 200,853 documents, create a small three-article index in OpenSearch Dashboards → Dev Tools (http://localhost:5602/app/dev_tools#/console).
PUT news
{
"settings": { "number_of_shards": 1, "number_of_replicas": 0 },
"mappings": {
"properties": {
"headline": { "type": "text" },
"category": { "type": "keyword" },
"date": { "type": "date" }
}
}
}
POST news/_bulk
{ "index": {} }
{ "headline": "Change Is Here. Climate Change.", "category": "POLITICS", "date": "2017-06-02" }
{ "index": {} }
{ "headline": "How To Cook A Perfect Steak At Home", "category": "TASTE", "date": "2015-11-14" }
{ "index": {} }
{ "headline": "Reuters: Markets Watch Fed Signals", "category": "BUSINESS", "date": "2018-01-09" }
Request 1 — full-text search:
GET news/_search
{
"query": { "match": { "headline": "climate change" } }
}
Expected result: one hit with a non-zero _score on the POLITICS article. On the real Elasticsearch corpus (module 5), the same request returns 2,834 results.
Request 2 — terms aggregation on category:
GET news/_search
{
"size": 0,
"aggs": {
"by_category": { "terms": { "field": "category", "size": 5 } }
}
}
Expected here: 3 buckets of one article each. On the real corpus, the same block would return POLITICS 32,739, WELLNESS 17,827, ENTERTAINMENT 16,058, TRAVEL 9,887, STYLE & BEAUTY 9,649.
Request 3 — relevance with bool:
GET news/_search
{
"query": {
"bool": {
"must": [{ "match": { "headline": "steak" } }],
"filter": [{ "term": { "category": "TASTE" } }]
}
}
}
Copy these three requests as-is into Kibana Dev Tools (http://localhost:5601) against the course's news index: the syntax is identical, only the numbers differ. That is the clearest demonstration: Query DSL is a de facto standard.
_xpack disappears on the OpenSearch side, replaced by _plugins/_security, _plugins/_sql, _plugins/_knn, _plugins/_ism. The _cat/* metric endpoints are kept identical.
OpenSearch Dashboards vs Kibana
The ergonomics are cousins but not identical. You find Discover, Visualize, Dashboards, Dev Tools, Stack Management. Kibana's Lens visualizations do not exist yet; OpenSearch offers the older Visualize editor plus VisBuilder. Saved object files (ndjson) exported from Kibana are not guaranteed to work in OpenSearch Dashboards, and vice versa. In practice, you rebuild dashboards in the target tool.
OpenSearch first-party plugins
They are all under Apache 2.0, all included in the image:
security: users, roles, ABAC, TLS, auditsql: SQL and PPL over indicesk-NN: exact and approximate vector searchneural-search: embedding integrationalerting: monitors and destinations (Slack, mail, webhook)anomaly-detection: unsupervised detectionindex-management(ISM): lifecycle policiesnotifications,ml-commons,observability,flow-framework
Decision criteria for Veille
Five concrete questions to settle as a team.
- Does the license block a planned use case? Veille operates as internal SaaS, does not redistribute the engine, does not sell a managed "OpenSearch as a Service". Elastic's free basic tier covers the need: SSPL does not hinder anything.
- Is the budget for a commercial license realistic? If Veille wants ELSER, OIDC authentication for its clients, or managed machine learning, it will need an Elastic Cloud or Platinum subscription. OpenSearch delivers the free equivalent on several of these axes.
- Which host? Elastic Cloud on one side, AWS OpenSearch Service on the other; both also exist on Aiven and Bonsai.
- Which search-AI ecosystem? ELSER and
semantic_textare a clear Elastic advantage in 2026; native k-NN and Neural Search on the OpenSearch side stay solid but demand more plumbing. - Which clients? If Karim writes his Python API against
elasticsearchv9, migrating means switching toopensearch-py(import, a few renamed options, a few different responses in the metadata).
Stay on Elasticsearch 9 today. Watch OpenSearch every six months: if the cloud cost gap crosses a threshold, or if a client demands a strict Apache 2.0 license, the door stays open.
Migrate an index
Two paths, the first is almost always the right one.
Path 1 — Re-import from the source
This is the clean path. Veille already has News_Category_Dataset_v2.json in data/; the kit's importer is a 200-line Python script. Point it at http://localhost:9201 instead of http://localhost:9200, remove authentication (or replace it with the one from the security plugin), and rerun. The mapping is rewritten identically in OpenSearch: nothing Elastic-specific in our schema. No risk of drift, reproducible tests, trivial rollback.
Path 2 — _reindex with source.remote
Useful when the source is far away or unreachable. Officially supported between Elasticsearch 7.10.2 and OpenSearch 1.x; beyond that, each version combination must be verified. You first need to whitelist the source host in opensearch.yml:
reindex.remote.whitelist: "elasticsearch:9200"
Then, in OpenSearch Dev Tools:
POST _reindex
{
"source": {
"remote": {
"host": "http://elasticsearch:9200",
"username": "elastic",
"password": "veille2026"
},
"index": "news"
},
"dest": { "index": "news" }
}
This launches a remote _reindex that queries the Elasticsearch API and pushes documents in local _bulk. For a 200,853-document corpus, count several minutes.
_reindex does not migrate mappings automatically. Create the target index with the correct mappings before the call, otherwise OpenSearch guesses a dynamic mapping that may differ from the original (for instance date guessed as text if the first date read is ambiguous).
Shut down cleanly
./lab.sh opensearch-down
This command stops the veille-opensearch and veille-os-dashboards containers without removing the os-data volume. The next ./lab.sh opensearch-up resumes state. A ./lab.sh reset would delete everything, including OpenSearch data.
Try it 1 — Compare cluster health
Start OpenSearch, run GET _cluster/health on both engines and note three differences in the JSON response.
Solution
cluster_name:veilleon the Elastic side,veille-oson the OpenSearch side.- The fields
active_shards_percent_as_numberandunassigned_primary_shardsare formatted differently depending on the version. - OpenSearch exposes
discovered_master(compatibility), Elasticsearch exposesdiscovered_cluster_managerand keepsdiscovered_masteras a synonym until 8, then renames it completely.
Try it 2 — Same terms on category
Write the same terms request on category (top 5) that would return POLITICS 32,739, WELLNESS 17,827, ENTERTAINMENT 16,058, TRAVEL 9,887, STYLE & BEAUTY 9,649 on the Elasticsearch corpus. Reminder: size: 0 to keep only the buckets.
Solution
GET news/_search
{
"size": 0,
"aggs": {
"categories": {
"terms": { "field": "category", "size": 5 }
}
}
}
The same request typed in Kibana Dev Tools would return exactly the five buckets above; in OpenSearch Dashboards on the three demo documents, it returns three buckets of one article.
Try it 3 — Internal note
Write for Inès an internal five-line note: "Should we stay on Elasticsearch?". Argue on license, security, cost and ecosystem.
Solution
One possible note: "We stay on Elasticsearch for now. The free basic tier covers our security need, ELSER and semantic_text accelerate the product's semantic search, and the migration to OpenSearch remains reversible via a re-import from News_Category_Dataset_v2.json. We reassess in six months based on (1) Elastic Cloud cost, (2) license requirements from our large-account clients, (3) the state of PPL and ML plugins on the OpenSearch side."
Key takeaways
- The fork dates to January 2021: Elastic changes the license from 7.11 onward, AWS forks from Elasticsearch 7.10.2 under Apache 2.0.
- 2024 brings AGPLv3 on the Elastic side (optional) and the Linux Foundation as OpenSearch's steward.
- Query DSL, aggregations,
_cat/*: nearly identical._xpackbecomes_plugins/_*. - PPL on OpenSearch, ES|QL on Elastic: two pipe languages aligned on the same need.
- Migrating an index is almost always done by re-importing from the source; remote
_reindexis a backup plan. - For Veille in 2026, Elasticsearch stays the default choice; OpenSearch comes in if license, cloud cost or a contractual requirement tips the scale.
- Do not mix clients:
elasticsearchv9 is not designed to talk to OpenSearch, and vice versa foropensearch-py.
Troubleshooting
opensearch-upfails withmax_map_countor OOM → same kernel constraint as Elasticsearch, memory to raise →./lab.sh doctorgives the sysctl command and the target value.- Port 9201 already taken → an old OpenSearch container from a previous workshop is still around →
./lab.sh opensearch-downthen relaunch. - Dashboards shows "OpenSearch cluster is not ready" → give it 1 to 2 min on first startup, or
./lab.sh logs opensearchto read the actual error message. - Remote
_reindexrefused (400reindex.remote.whitelist) → the source host is not listed inopensearch.yml→ addreindex.remote.whitelist: "elasticsearch:9200"and restart OpenSearch.
Further reading
- OpenSearch documentation: https://docs.opensearch.org/
- Query DSL and PPL: https://docs.opensearch.org/latest/query-dsl/
- Foundation and governance: https://opensearch.org/foundation/
- Migrating from Elasticsearch: https://docs.opensearch.org/latest/migration-assistant/
- Fork announcement blog (historical reference): https://opensearch.org/blog/introducing-opensearch/
Next module: graph databases, Neo4j and first steps in Cypher — you will lay down the Veille team's mini-graph before loading the News graph.