Download index.html from getreadystack/opensearch-migration-check: direct link, hf CLI and curl.
- Browser
- Download file 45.1 kB
-
https://huggingface.co/spaces/getreadystack/opensearch-migration-check/resolve/main/index.html
- Command line
-
hf download hf://spaces/getreadystack/opensearch-migration-check/index.html
-
curl -L -o index.html https://huggingface.co/spaces/getreadystack/opensearch-migration-check/resolve/main/index.html
45.1 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width,initial-scale=1"> | |
| <title>Elasticsearch OpenSearch Migration — check your stack for free</title> | |
| <meta name="description" content="This free browser tool flags Elasticsearch and Kibana stack elements that break migration to OpenSearch and shows an OpenSearch replacement."> | |
| <style> | |
| :root{color-scheme:light}body{margin:0;font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif;background:#faf9f5;color:#1a1512} | |
| .wrap{max-width:960px;margin:0 auto;padding:28px 18px 60px}h1{font-size:26px;margin:0 0 6px}.lede{color:#4a403a;margin:0 0 16px} | |
| textarea{width:100%;min-height:260px;font:13px/1.45 ui-monospace,Menlo,Consolas,monospace;padding:12px;border:1px solid #d9d0c4;border-radius:8px;box-sizing:border-box} | |
| .bar{display:flex;gap:10px;align-items:center;margin:12px 0}button{font:600 15px system-ui;padding:10px 18px;border:0;border-radius:8px;background:#1a1512;color:#fff;cursor:pointer} | |
| input[type=date]{font:14px system-ui;padding:8px;border:1px solid #d9d0c4;border-radius:6px} | |
| table{border-collapse:collapse;width:100%;font-size:14px;margin-top:10px}th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #eee;vertical-align:top}th{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:#7a6e65} | |
| .sev.error{color:#a8551f;font-weight:700}.sev.warn,.sev.warning{color:#96691f}.ok{color:#2a6446;font-weight:600}.sum{font-weight:600} | |
| .note{font-size:13px;color:#7a6e65;margin-top:18px}#paid{margin-top:22px;padding:14px 16px;border:1px solid #e2d8cb;border-radius:10px;background:#fffdfa} | |
| </style> | |
| <script defer data-auto-init src="https://cdn.jsdelivr.net/npm/@polar-sh/checkout@latest/dist/embed.global.js"></script> | |
| <script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script> | |
| <!--yj-canon-s101--><link rel="canonical" href="https://getreadystack.com/tools/opensearch-migration-check"><style data-yj-mfit="1">#out{max-width:100%}#out td,#out th{overflow-wrap:anywhere}#out pre,#out code{white-space:pre-wrap;overflow-wrap:anywhere}@media (max-width:640px){table{display:block;max-width:100%;overflow-x:auto}pre{max-width:100%;overflow-x:auto}code,a{overflow-wrap:anywhere}img,svg,video,canvas,iframe{max-width:100%}textarea,input,select{max-width:100%;box-sizing:border-box}.yj-lgbar{position:static;justify-content:flex-end;margin:0 0 10px}#out table:not(:has(tr>:nth-child(4))){display:table;table-layout:fixed;width:100%}#out table:has(tr>:nth-child(4)),#out table:has(tr>:nth-child(4)) tbody,#out table:has(tr>:nth-child(4)) tr,#out table:has(tr>:nth-child(4)) td{display:block;width:auto;overflow:visible}#out table:has(tr>:nth-child(4)) thead{display:none}#out table:has(tr>:nth-child(4)) tr{padding:10px 0;border-bottom:1px solid rgba(128,128,128,.28)}#out table:has(tr>:nth-child(4)) td{border:0;padding:1px 0}#out table:has(tr>:nth-child(4)) td:nth-child(-n+3){display:inline-block;margin-right:10px;font-size:12px;opacity:.78}}</style> | |
| </head> | |
| <body> | |
| <div class="wrap"> | |
| <h1>Elasticsearch OpenSearch Migration — check your stack for free</h1> | |
| <p class="lede">OpenSearch migration check for Elasticsearch and Kibana stacks: flags Elastic-licensed images, xpack settings, 7.14+ clients, ILM, dense_vector and Beats that break the cutover, with the OpenSearch replacement. Runs entirely in your browser — nothing is uploaded.</p> | |
| <textarea id="src" spellcheck="false"># docker-compose.yml — logging stack before the OpenSearch cutover | |
| services: | |
| elasticsearch: | |
| image: docker.elastic.co/elasticsearch/elasticsearch:8.15.0 | |
| environment: | |
| - discovery.type=single-node | |
| - xpack.security.enabled=true | |
| - ELASTIC_PASSWORD=changeme | |
| - ES_JAVA_OPTS=-Xms1g -Xmx1g | |
| ports: | |
| - "9200:9200" | |
| volumes: | |
| - esdata:/usr/share/elasticsearch/data | |
| kibana: | |
| image: docker.elastic.co/kibana/kibana:8.15.0 | |
| environment: | |
| - ELASTICSEARCH_HOSTS=http://elasticsearch:9200 | |
| ports: | |
| - "5601:5601" | |
| depends_on: | |
| - elasticsearch | |
| filebeat: | |
| image: docker.elastic.co/beats/filebeat:8.15.0 | |
| volumes: | |
| - ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro | |
| depends_on: | |
| - elasticsearch | |
| volumes: | |
| esdata:</textarea> | |
| <div class="bar"><button id="go">Check</button><label>as of <input type="date" id="today"></label></div> | |
| <div id="out" aria-live="polite"></div> | |
| <div id="paid"><strong>Free here:</strong> Checks the open file (compose, elasticsearch.yml, Kibana config, Logstash pipeline, requirements, package.json, mapping) and names every line that breaks an OpenSearch cutover, with the replacement. <strong>In the editor:</strong> Scans the whole repository in one pass and exports one migration checklist (Markdown) per file, ready for the change ticket. — that part asks for a licence key. <a href="https://getreadystack.com/api/buy/cl/polar_cl_SfGssVKywn94tRL3JwJOi6FlwhCvHAxRfTviy2i3VXP" data-polar-checkout data-polar-checkout-theme="light">Get the full version — $29</a> · $29 once · one licence key per person or team seat. Elastic Cloud Hosted Standard starts at $99 per month.</div> | |
| <p class="note">Same engine as the VS Code extension, byte for byte.</p> | |
| </div> | |
| <script>var OSM_RULES = [ | |
| {"id":"es-image","sev":"error","re":"(?:image:\\s*[\"']?|^\\s*FROM\\s+|repository:\\s*[\"']?)(?:docker\\.elastic\\.co\\/elasticsearch\\/)?elasticsearch(?:-oss)?(?::([\\w.\\-]+))?","flags":"i","msg":"Elasticsearch image (SSPL or Elastic License 2.0 since 7.11, not Apache-2.0)","fix":"opensearchproject/opensearch:2 (Apache-2.0); re-index or snapshot-restore the data, OpenSearch cannot open 8.x indices","eos":{"tag_prefix":"7.","date":"2026-01-15","before":"Elasticsearch 7.17 support ends 2026-01-15 ({days} days left)","after":"Elasticsearch 7.x is out of Elastic support since 2026-01-15 ({days} days)"}}, | |
| {"id":"kibana-image","sev":"error","re":"(?:image:\\s*[\"']?|^\\s*FROM\\s+|repository:\\s*[\"']?)(?:docker\\.elastic\\.co\\/kibana\\/)?kibana(?::[\\w.\\-]+)?","flags":"i","msg":"Kibana image; saved objects from Kibana newer than 7.10.2 do not import into OpenSearch Dashboards","fix":"opensearchproject/opensearch-dashboards:2 and rebuild dashboards"}, | |
| {"id":"beats-image","sev":"error","re":"(?:image:\\s*[\"']?|^\\s*FROM\\s+|repository:\\s*[\"']?)(?:docker\\.elastic\\.co\\/beats\\/)?(?:filebeat|metricbeat|packetbeat|heartbeat|auditbeat|winlogbeat|elastic-agent)(?::[\\w.\\-]+)?","flags":"i","msg":"Elastic Beats / Elastic Agent shipper; OpenSearch's compatibility table covers only OSS Beats up to 7.12.1","fix":"ship with Fluent Bit, OpenSearch Data Prepper or filebeat-oss 7.12.1"}, | |
| {"id":"logstash-image","sev":"warn","re":"(?:image:\\s*[\"']?|^\\s*FROM\\s+)(?:docker\\.elastic\\.co\\/logstash\\/)?logstash(?::[\\w.\\-]+)?","flags":"i","msg":"Logstash image without the OpenSearch output plugin","fix":"opensearchproject/logstash-oss-with-opensearch-output-plugin"}, | |
| {"id":"elastic-password","sev":"error","re":"\\bELASTIC_PASSWORD\\b","flags":"","msg":"ELASTIC_PASSWORD is ignored by OpenSearch; since 2.12 the security demo setup refuses to start without an initial admin password","fix":"OPENSEARCH_INITIAL_ADMIN_PASSWORD=\u003cstrong password\u003e"}, | |
| {"id":"xpack-security","sev":"error","re":"\\bxpack\\.security\\.[\\w.]+","flags":"","msg":"xpack.security.* setting; OpenSearch rejects unknown settings at startup","fix":"plugins.security.* in opensearch.yml (or DISABLE_SECURITY_PLUGIN=true for local dev only)"}, | |
| {"id":"xpack-other","sev":"error","re":"\\bxpack\\.(?!security\\b)[\\w.]+","flags":"","msg":"xpack.* setting (ML, Watcher, monitoring, licence); OpenSearch rejects unknown settings at startup","fix":"remove it; ML maps to the Anomaly Detection plugin, Watcher to the Alerting plugin"}, | |
| {"id":"es-hosts","sev":"error","re":"\\bELASTICSEARCH_(?:HOSTS|URL|USERNAME|PASSWORD|SERVICEACCOUNTTOKEN)\\b|^\\s*elasticsearch\\.(?:hosts|username|password|serviceAccountToken|ssl\\.[\\w.]+)\\s*:","flags":"","msg":"Kibana connection setting; OpenSearch Dashboards does not read it","fix":"OPENSEARCH_HOSTS / opensearch.hosts in opensearch_dashboards.yml"}, | |
| {"id":"py-client","sev":"error","re":"^\\s*[\"']?elasticsearch(?:-dsl)?(?:\\[[\\w,]+\\])?\\s*(?:==|\u003e=|\u003c=|~=|!=|\u003e|\u003c|[\"']?,?\\s*$)|^\\s*(?:from|import)\\s+elasticsearch(?:_dsl)?\\b","flags":"","msg":"Python elasticsearch client; 7.14.0 and later raise UnsupportedProductError against any server that is not Elasticsearch","fix":"opensearch-py (from opensearchpy import OpenSearch)"}, | |
| {"id":"js-client","sev":"error","re":"[\"']@elastic\\/elasticsearch[\"']","flags":"","msg":"Node @elastic/elasticsearch client; 7.14 and later run the same product check and refuse OpenSearch","fix":"@opensearch-project/opensearch"}, | |
| {"id":"java-client","sev":"error","re":"co\\.elastic\\.clients|org\\.elasticsearch\\.client","flags":"","msg":"Elastic Java client; 7.14 and later refuse a server that is not Elasticsearch","fix":"org.opensearch.client:opensearch-java"}, | |
| {"id":"go-client","sev":"error","re":"github\\.com\\/elastic\\/go-elasticsearch","flags":"","msg":"go-elasticsearch client; 7.14 and later refuse a server that is not Elasticsearch","fix":"github.com/opensearch-project/opensearch-go"}, | |
| {"id":"ruby-client","sev":"error","re":"gem\\s+[\"']elasticsearch(?:-[\\w]+)?[\"']","flags":"","msg":"elasticsearch-ruby gem; 7.14 and later refuse a server that is not Elasticsearch","fix":"gem 'opensearch-ruby'"}, | |
| {"id":"logstash-output","sev":"error","re":"^\\s*elasticsearch\\s*\\{","flags":"","msg":"Logstash elasticsearch output block","fix":"opensearch { hosts =\u003e [...] } with the logstash-output-opensearch plugin"}, | |
| {"id":"ilm","sev":"warn","re":"\\bindex\\.lifecycle\\.|\\/_ilm\\/|\\bsetup\\.ilm\\.|\\bilm_policy\\b","flags":"","msg":"Index Lifecycle Management (ILM) is Elastic-only","fix":"rewrite as an ISM policy (PUT _plugins/_ism/policies/\u003cname\u003e) and attach it with ism_template"}, | |
| {"id":"dense-vector","sev":"error","re":"[\"']?type[\"']?\\s*:\\s*[\"']?dense_vector\\b","flags":"","msg":"dense_vector field type does not exist in OpenSearch","fix":"knn_vector with \"dimension\" and index.knn: true on the index"}, | |
| {"id":"flattened","sev":"warn","re":"[\"']?type[\"']?\\s*:\\s*[\"']?flattened\\b","flags":"","msg":"flattened field type does not exist in OpenSearch","fix":"flat_object (OpenSearch 2.7 and later)"}, | |
| {"id":"semantic-sparse","sev":"error","re":"[\"']?type[\"']?\\s*:\\s*[\"']?(?:semantic_text|sparse_vector)\\b","flags":"","msg":"Elastic semantic_text / sparse_vector field type","fix":"neural-search plugin: knn_vector or rank_features filled by an ingest pipeline"}, | |
| {"id":"esql","sev":"error","re":"\\/_query(?=[?\\s\"'\\/]|$)|\\bES\\|QL\\b|\\besql\\b","flags":"i","msg":"ES|QL query (Elastic-only language)","fix":"rewrite as PPL or SQL (POST _plugins/_ppl or _plugins/_sql)"}, | |
| {"id":"security-api","sev":"warn","re":"\\/_security\\/(?:api_key|user|role|role_mapping|service)\\b","flags":"","msg":"Elastic security REST API path","fix":"_plugins/_security/api/... (internalusers, roles, rolesmapping)"}, | |
| {"id":"cloud-id","sev":"error","re":"\\bcloud_id\\b|\\bcloud\\.id\\s*:|\\bELASTIC_CLOUD_ID\\b|\\bcloudId\\s*:","flags":"","msg":"Elastic Cloud ID has no OpenSearch equivalent","fix":"connect with the OpenSearch endpoint URL and credentials"}, | |
| {"id":"xpack-endpoint","sev":"error","re":"\\/_xpack\\b|\\/_license\\b","flags":"","msg":"_xpack / _license API is Elastic-only","fix":"drop the call; OpenSearch has no licence API"}, | |
| {"id":"elastic-helm","sev":"warn","re":"helm\\.elastic\\.co","flags":"","msg":"Elastic Helm repository","fix":"https://opensearch-project.github.io/helm-charts/ (charts opensearch and opensearch-dashboards)"}, | |
| {"id":"master-role","sev":"warn","re":"\\bcluster\\.initial_master_nodes\\b|\\bnode\\.roles\\s*[:=]\\s*\\[?[^\\]\\n]*\\bmaster\\b","flags":"","msg":"master naming is deprecated in OpenSearch 2.x","fix":"cluster.initial_cluster_manager_nodes and node.roles: [cluster_manager]"}, | |
| {"id":"eck-crd","sev":"error","re":"apiVersion:\\s*(?:elasticsearch|kibana|beat|agent|logstash)\\.k8s\\.elastic\\.co","flags":"","msg":"ECK custom resource; the Elastic operator does not run OpenSearch","fix":"OpenSearch Kubernetes Operator (opensearch.opster.io/v1 OpenSearchCluster)"} | |
| ] | |
| ;</script> | |
| <script>/* OpenSearch migration check: one rule per Elastic-only line. Same file runs in VS Code and in the browser. */ | |
| (function () { | |
| var RULES = (typeof module !== 'undefined') ? require('./rules.json') : window.OSM_RULES; | |
| var DAY = 86400000; | |
| function toDay(v) { | |
| var s = String(v || '').slice(0, 10); | |
| var d = new Date(s + 'T00:00:00Z'); | |
| if (isNaN(d.getTime())) { var n = new Date(); d = new Date(Date.UTC(n.getFullYear(), n.getMonth(), n.getDate())); } | |
| return d; | |
| } | |
| function eosNote(rule, m, today) { | |
| var e = rule.eos; | |
| if (!e || !m[1] || m[1].indexOf(e.tag_prefix) !== 0) return ''; | |
| var cut = toDay(e.date); | |
| var days = Math.round(Math.abs(cut - today) / DAY); | |
| return ' ' + (today < cut ? e.before : e.after).replace('{days}', days) + '.'; | |
| } | |
| function check(text, opts) { | |
| opts = opts || {}; | |
| var today = toDay(opts.today); | |
| var lines = String(text || '').split(/\r?\n/); | |
| var findings = []; | |
| lines.forEach(function (line, i) { | |
| var t = line.trim(); | |
| if (!t || t.charAt(0) === '#' || t.indexOf('//') === 0) return; | |
| RULES.forEach(function (r) { | |
| var m = new RegExp(r.re, r.flags || '').exec(line); | |
| if (!m) return; | |
| findings.push({ check: r.id, sev: r.sev, line: i + 1, | |
| msg: r.msg + '.' + eosNote(r, m, today) + ' Fix: ' + r.fix + '.', | |
| text: t.slice(0, 120), fix: r.fix }); | |
| }); | |
| }); | |
| return { findings: findings, errors: findings.filter(function (f) { return f.sev === 'error'; }).length, | |
| warnings: findings.filter(function (f) { return f.sev === 'warn'; }).length, rules: RULES.length }; | |
| } | |
| var api = { engine: { check: check }, RULES: RULES, RULE_COUNT: RULES.length }; | |
| if (typeof window !== 'undefined') window.OSMENGINE = api; | |
| if (typeof module !== 'undefined') module.exports = api; | |
| })(); | |
| </script> | |
| <script>// ⛔뼈대 — kit/scaffold.py 가 찍는다 (s143). findings [{check,sev,msg,line?}] → 글/HTML. 같은 파일이 Node 와 브라우저에서 산다. | |
| ; | |
| function escapeHtml(s) { return String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); } | |
| function summaryLine(res) { | |
| const f = (res && res.findings) || []; const e = f.filter(function (x) { return String(x.sev || 'error').toLowerCase().startsWith('err'); }).length; | |
| return f.length ? (f.length + ' finding' + (f.length === 1 ? '' : 's') + ' (' + e + ' error' + (e === 1 ? '' : 's') + ')') : 'Clean — no findings.'; | |
| } | |
| function toText(res, label) { | |
| const f = (res && res.findings) || []; | |
| const lines = ['== ' + (label || '') + ' — ' + summaryLine(res)]; | |
| f.forEach(function (x) { lines.push(' ' + String(x.sev || 'error').toUpperCase().slice(0, 4) + ' ' + (x.line ? 'L' + x.line + ' ' : '') + (x.check || x.id || '') + ' ' + (x.msg || x.message || '')); }); | |
| return lines.join('\n'); | |
| } | |
| function toHtml(res) { | |
| const f = (res && res.findings) || []; | |
| if (!f.length) return '<p class="ok">' + escapeHtml(summaryLine(res)) + '</p>'; | |
| const rows = f.map(function (x) { return '<tr><td class="sev ' + escapeHtml(String(x.sev || 'error')) + '">' + escapeHtml(String(x.sev || 'error')) + '</td><td>' + (x.line ? 'L' + escapeHtml(x.line) : '') + '</td><td><code>' + escapeHtml(x.check || x.id || '') + '</code></td><td>' + escapeHtml(x.msg || x.message || '') + '</td></tr>'; }).join(''); | |
| return '<p class="sum">' + escapeHtml(summaryLine(res)) + '</p><table><thead><tr><th>Level</th><th>Line</th><th>Check</th><th>What</th></tr></thead><tbody>' + rows + '</tbody></table>'; | |
| } | |
| var OSMREPORT = { escapeHtml: escapeHtml, summaryLine: summaryLine, toText: toText, toHtml: toHtml }; | |
| if (typeof module !== 'undefined' && module.exports) module.exports = OSMREPORT; | |
| else window.OSMREPORT = OSMREPORT; | |
| </script> | |
| <script> | |
| (function(){ | |
| var src=document.getElementById('src'), out=document.getElementById('out'), go=document.getElementById('go'), today=document.getElementById('today'); | |
| today.value=new Date().toISOString().slice(0,10); | |
| function run(){ try { var res=window.OSMENGINE.engine.check(src.value,{today:today.value}); out.innerHTML=window.OSMREPORT.toHtml(res); } catch(e){ out.innerHTML='<p class="sev error">Could not run: '+String(e.message||e)+'</p>'; } } | |
| go.onclick=run; if (src.value.trim()) run(); | |
| })(); | |
| </script> | |
| <section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://marketplace.visualstudio.com/items?itemName=ReadyStack.opensearch-migration-check" rel="noopener" data-yj-install-door="mp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'opensearch-migration-check',src:'mp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">VS Code Marketplace</a><a href="https://open-vsx.org/extension/readystack/opensearch-migration-check" rel="noopener" data-yj-install-door="ovsx" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'opensearch-migration-check',src:'ovsx',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Open VSX (Cursor, VSCodium)</a><pre style="margin:.5rem 0 0;padding:.6rem .8rem;background:#f3f5f8;border-radius:8px;font-size:.9rem;overflow:auto">ext install ReadyStack.opensearch-migration-check</pre><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://getreadystack.com/api/buy/team" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section> | |
| <section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get one email when this rule changes</b><div style="opacity:.78;margin:.35rem 0 .8rem">We watch the regulation and vendor sources behind OpenSearch Migration Check: Elasticsearch & Kibana cutover lint every day. When a rule changes, you get a single email with what changed and the updated check. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Notify me</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">You're on the list. One email when it changes.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"opensearch-migration-check",lang:"en",source:"rule_alert"+((b.closest&&b.closest("section")&&b.closest("section").getAttribute("data-yj-moved"))?"_at_result":"")})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script><script>(function(){var e=document.getElementById("yjae"),s=e&&e.closest?e.closest("section"):null,o=document.getElementById("out");if(!s||!o)return;var d=false;function mv(ev){if(d)return;if(ev&&ev.target&&s.contains(ev.target))return;d=true;var b=o;while(b.parentNode&&b.parentNode!==document.body&&b.parentNode.tagName!=="MAIN"&&b.parentNode.tagName!=="ARTICLE")b=b.parentNode;if(!b.parentNode||b===s||b.contains(s))return;try{b.parentNode.insertBefore(s,b.nextSibling);s.setAttribute("data-yj-moved","1");}catch(_){}}document.addEventListener("input",mv,true);document.addEventListener("change",mv,true);document.addEventListener("click",function(ev){var b=ev.target&&ev.target.closest?ev.target.closest("button,input[type=submit]"):null;if(b&&!b.closest("[data-yj],[data-yj-cta],[data-yj-install],[data-yj-alert],.yj-find,.yj-related,#yj-ask,.yjp"))mv(ev);},true);})();</script></section> | |
| <section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the full version <span class="yj-price">$29</span></h2><p class="yj-cta-paid" style="margin:.25rem 0 .6rem;color:#1f2a37">Scans the whole repository in one pass and exports one migration checklist (Markdown) per file, ready for the change ticket.</p><p class="yj-cta-pay" style="margin:.25rem 0 1rem;color:#5b6b7c;font-size:.94rem">One payment, one licence key for this tool. The key is shown right after payment.</p><p class="yj-cta-anchor" style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">Elastic Cloud Hosted Standard starts at $99 per month (elastic.co/pricing/cloud-hosted).</p><a href="https://getreadystack.com/api/buy/cl/polar_cl_SfGssVKywn94tRL3JwJOi6FlwhCvHAxRfTviy2i3VXP" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section> | |
| <!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script> | |
| <script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script> | |
| <script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&","<":"<",">":">",'"':"""}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div><section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><p style="font:400 13px/1.5 system-ui,sans-serif;color:#6b7680;margin:0 0 14px"><time datetime="2026-09-29">Updated 2026-09-29</time> · ReadyStack</p><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","dateModified":"2026-09-29","author":{"@type":"Organization","name":"ReadyStack"}}</script><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Worked example</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 16px;color:#5b6570">Real numbers from this tool, line by line.</p><div class="yj-article"><p><img src="https://getreadystack.com/img/promo/sku391117_result_card.jpg" alt="OpenSearch Migration Check: Elasticsearch & Kibana cutover lint" loading="lazy" style="max-width:100%;border-radius:6px"></p> | |
| <p>Migrate from Elasticsearch to OpenSearch: 6 lines in a sample docker-compose.yml that break the cutover</p> | |
| <p>6 lines in a sample Elasticsearch 8.15.0 docker-compose.yml break an OpenSearch cutover, and platform engineers in teams moving off Elastic find most of them only when the new node refuses to start. Elastic moved Elasticsearch and Kibana from Apache-2.0 to SSPL / Elastic License 2.0 with version 7.11, and Elasticsearch 7.17 reached end of support on 2026-01-15. OpenSearch forked from 7.10.2 and stayed Apache-2.0, so it is where many self-managed clusters go next.</p> | |
| <h3>The sample file</h3> | |
| <p>The stack is the one you find in many repositories: one Elasticsearch node, Kibana, and Filebeat shipping container logs, all on 8.15.0.</p> | |
| <pre><code>elasticsearch: | |
| image: docker.elastic.co/elasticsearch/elasticsearch:8.15.0 | |
| environment: | |
| - xpack.security.enabled=true | |
| - ELASTIC_PASSWORD=changeme | |
| kibana: | |
| image: docker.elastic.co/kibana/kibana:8.15.0 | |
| environment: | |
| - ELASTICSEARCH_HOSTS=http://elasticsearch:9200 | |
| filebeat: | |
| image: docker.elastic.co/beats/filebeat:8.15.0</code></pre> | |
| <h3>The 6 findings</h3> | |
| <table><thead><tr><th>Line that breaks</th><th>OpenSearch fix</th></tr></thead><tbody><tr><td>elasticsearch:8.15.0 image</td><td>opensearchproject/opensearch:2</td></tr><tr><td>xpack.security.enabled=true</td><td>plugins.security.*</td></tr><tr><td>ELASTIC_PASSWORD</td><td>OPENSEARCH_INITIAL_ADMIN_PASSWORD</td></tr><tr><td>kibana:8.15.0 image</td><td>opensearch-dashboards:2</td></tr><tr><td>ELASTICSEARCH_HOSTS</td><td>OPENSEARCH_HOSTS</td></tr><tr><td>filebeat:8.15.0 image</td><td>Fluent Bit or Data Prepper</td></tr></tbody></table> | |
| <p>Why each one matters:</p> | |
| <ul><li><strong>xpack settings.</strong> OpenSearch refuses to start when it reads a setting it does not know. One forgotten <code>xpack.security.enabled</code> stops the node at boot.</li><li><strong>The admin password.</strong> From OpenSearch 2.12 on, the security demo setup needs <code>OPENSEARCH_INITIAL_ADMIN_PASSWORD</code>. <code>ELASTIC_PASSWORD</code> is simply ignored.</li><li><strong>Kibana.</strong> OpenSearch Dashboards reads <code>OPENSEARCH_HOSTS</code>, not <code>ELASTICSEARCH_HOSTS</code>, and saved objects from Kibana newer than 7.10.2 do not import.</li><li><strong>Beats.</strong> OpenSearch's compatibility table covers OSS Beats up to 7.12.1. A Filebeat 8.15.0 container needs to become Fluent Bit, Data Prepper or filebeat-oss 7.12.1.</li></ul> | |
| <p>Rewritten for OpenSearch 2.17.0, the same file gives 0 findings.</p> | |
| <h3>The part grep misses: clients</h3> | |
| <p>Search the repository for <code>elasticsearch</code> and you get every service name, volume path and URL. The dangerous hits are in the dependency files. elasticsearch-py 7.14.0 added a product check that raises <code>UnsupportedProductError</code> on any server that is not Elasticsearch, and the Node, Java, Go and Ruby clients do the same. A <code>requirements.txt</code> with <code>elasticsearch==8.15.1</code> and <code>elasticsearch-dsl==8.15.3</code> gives 2 findings; the fix is <code>opensearch-py</code>.</p> | |
| <h3>Mappings and APIs</h3> | |
| <p>Index templates carry their own blockers: <code>dense_vector</code> becomes <code>knn_vector</code> with <code>index.knn: true</code>, <code>flattened</code> becomes <code>flat_object</code> (OpenSearch 2.7 and later), ILM policies become ISM policies under <code>_plugins/_ism</code>, and ES|QL queries on <code>/_query</code> need to be rewritten as PPL or SQL.</p> | |
| <h3>Checking your own files</h3> | |
| <p>OpenSearch Migration Check is a VS Code extension with 25 rules covering images, settings, clients, pipelines, mappings and APIs. Open a file and the findings appear in the Problems panel with the line number and the replacement. For a 7.x image it also counts the days since the 2026-01-15 end of support, from the date you run it. The same engine runs in the free web version in the browser.</p> | |
| <p>Staying on Elastic hosting is a monthly bill: Elastic Cloud Hosted Standard starts at $99 per month. It does not move data; it lists the lines to change first.</p></div><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:30px 0 10px;color:#1b1f24">15 seconds — what it actually does</h2><video controls muted playsinline preload="none" poster="/img/promo/sku391117_result_card.jpg" style="width:100%;max-width:360px;border-radius:10px;display:block;margin:0 auto"><source src="/media/sku391117_short.mp4" type="video/mp4"></video><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does OpenSearch Migration Check do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads a docker-compose.yml, elasticsearch.yml, Kibana config, Logstash pipeline, client dependency file or index mapping and flags every line that breaks a move from Elasticsearch and Kibana to OpenSearch: Elastic images, xpack settings, 7.14+ clients, ILM, dense_vector and Beats. Each finding has the line number and the OpenSearch replacement. It has 25 rules and runs locally.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is OpenSearch Migration Check for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Platform engineers, SREs and DevOps teams moving a self-managed Elasticsearch 7.x or 8.x stack to OpenSearch or Amazon OpenSearch Service, usually because of the SSPL / Elastic License change in 7.11 or because Elasticsearch 7.17 reached end of support on 2026-01-15. It also helps developers switching application clients to opensearch-py or the OpenSearch JavaScript client.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why not just grep for elasticsearch or ask a chatbot?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Grep for elasticsearch hits every service name, volume path and URL, so the real blockers drown. A chatbot rarely knows that Elastic clients from 7.14 on raise UnsupportedProductError against OpenSearch, that OpenSearch refuses to start on an unknown xpack setting, or that OpenSearch 2.12 needs OPENSEARCH_INITIAL_ADMIN_PASSWORD. This check knows those lines and gives the replacement for each.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what does the paid version add?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">The free version checks any open file with all 25 rules, with line numbers and fixes, and the web version does the same in the browser. Nothing is cut or time-limited. The paid version, $29 once, scans the whole repository in one pass and exports one Markdown migration checklist per file, ready to attach to the change ticket.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does the alternative cost?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Staying on Elastic's hosted offering costs money every month: Elastic Cloud Hosted Standard starts at $99 per month according to Elastic's pricing page. The free check costs nothing and the repository-wide version is a one-time $29, one licence key per person or team seat. It does not replace migrating the data, it finds the config and code lines to change first.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does OpenSearch Migration Check do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads a docker-compose.yml, elasticsearch.yml, Kibana config, Logstash pipeline, client dependency file or index mapping and flags every line that breaks a move from Elasticsearch and Kibana to OpenSearch: Elastic images, xpack settings, 7.14+ clients, ILM, dense_vector and Beats. Each finding has the line number and the OpenSearch replacement. It has 25 rules and runs locally."}}, {"@type": "Question", "name": "Who is OpenSearch Migration Check for?", "acceptedAnswer": {"@type": "Answer", "text": "Platform engineers, SREs and DevOps teams moving a self-managed Elasticsearch 7.x or 8.x stack to OpenSearch or Amazon OpenSearch Service, usually because of the SSPL / Elastic License change in 7.11 or because Elasticsearch 7.17 reached end of support on 2026-01-15. It also helps developers switching application clients to opensearch-py or the OpenSearch JavaScript client."}}, {"@type": "Question", "name": "Why not just grep for elasticsearch or ask a chatbot?", "acceptedAnswer": {"@type": "Answer", "text": "Grep for elasticsearch hits every service name, volume path and URL, so the real blockers drown. A chatbot rarely knows that Elastic clients from 7.14 on raise UnsupportedProductError against OpenSearch, that OpenSearch refuses to start on an unknown xpack setting, or that OpenSearch 2.12 needs OPENSEARCH_INITIAL_ADMIN_PASSWORD. This check knows those lines and gives the replacement for each."}}, {"@type": "Question", "name": "What is free and what does the paid version add?", "acceptedAnswer": {"@type": "Answer", "text": "The free version checks any open file with all 25 rules, with line numbers and fixes, and the web version does the same in the browser. Nothing is cut or time-limited. The paid version, $29 once, scans the whole repository in one pass and exports one Markdown migration checklist per file, ready to attach to the change ticket."}}, {"@type": "Question", "name": "What does the alternative cost?", "acceptedAnswer": {"@type": "Answer", "text": "Staying on Elastic's hosted offering costs money every month: Elastic Cloud Hosted Standard starts at $99 per month according to Elastic's pricing page. The free check costs nothing and the repository-wide version is a one-time $29, one licence key per person or team seat. It does not replace migrating the data, it finds the config and code lines to change first."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:391117,slug:"opensearch-migration-check",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/devicetree-config-language-pack"><span class="yj-rel-t">DeviceTree Config Audit: 23 dtc-Style Rules, 50 Snippets</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/podman-migration-check"><span class="yj-rel-t">Podman Migration Check — Docker Desktop ties</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/about-page-template"><span class="yj-rel-t">About Page Template — 6 Finished About Pages for Small Service Businesses (Sel</span><span class="yj-rel-p">$49</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/dotnet-eol-support-gate"><span class="yj-rel-t">.NET EOL & CRA Support Window Gate</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/robots-txt-ai-crawler-audit"><span class="yj-rel-t">AI Crawler Rules - robots.txt Audit for AI Search</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/aks-eks-support-cliff-lint"><span class="yj-rel-t">AKS LTS & EKS Extended Support Lint for Terraform</span><span class="yj-rel-p">$29</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section></body> | |
| </html> | |