# Elasticsearch and OpenSearch tutorial: code and real outputs

Run date: 2026-11-09. Docker 24.0.6 (Docker Desktop, arm64, about 8 GB RAM, other containers left alone). Everything Python runs in Docker (`python:3.12-slim`, Python 3.12.16, `requests==2.32.3`, see `pip-freeze.txt`). Query vectors are computed on the host with the Ollama HTTP API (`embed_queries.py`, standard library only), so the containers never talk to Ollama.

Pins (verified the same day on the vendor pages):
- Elasticsearch `docker.elastic.co/elasticsearch/elasticsearch:9.5.6` (Lucene 10.5.2), Kibana `docker.elastic.co/kibana/kibana:9.5.6`. 9.5.6 is the top entry of the Elastic release notes page on 2026-11-09.
- OpenSearch `opensearchproject/opensearch:3.9.0` (Lucene 10.5.1), OpenSearch Dashboards `opensearchproject/opensearch-dashboards:3.9.0`. 3.9.0 is the `latest` tag on Docker Hub, pushed 2026-10-26.

Security is OFF on both stacks (`xpack.security.enabled=false`, `DISABLE_SECURITY_PLUGIN=true`), ports are bound to 127.0.0.1 and heaps are 512m with `mem_limit` 1.5g (Kibana and Dashboards 1g). That is for a local demo only. Do not copy it to a server.

Input: `chunks.jsonl` (not included, 3.4 MB with embeddings). 304 rows, 52 pages of webcrawlerapi.com/docs, nomic-embed-text 768 dims, unit-length vectors, sha256 `7c62db9258ba7b33bc75bf3e0593b0a7ffc73d0b0fb11da68db0eb27ebe4e145` (same file as the Chroma and sqlite-vec posts). `qvecs.json` holds the 7 precomputed query vectors (query text prefixed with `search_query: `). `build_chunks.py` and `questions.json` come from the Chroma post. `build_chunks.py` was NOT run for this post.

Files: `compose.es.yml`, `compose.os.yml`, `Dockerfile`, `common.py`, `run_es.sh` (runs any script on the compose network; works for both engines through `NET`, `ENGINE_URL`, `ENGINE`), `es_setup.py`, `es_load.py`, `es_query.py`, `es_extra.py` (filters, highlight, re-crawl update; also the OpenSearch run with `ENGINE=os`), `os_probe.py`, `os_setup.py`, `os_load.py`, `os_query.py`, `knn_top5.py`, `overlap.py`, `kibana_seed.sh`, `run.sh`.
`*.out` files are the real output of the run. `es_query.out` ran on the Basic license (RRF returns 403, the `rrf` column is the Python fallback). `es_query_trial.out` ran on a fresh cluster after `POST _license/start_trial?acknowledge=true` (native RRF works). `t1-es-setup.out` is the compose and version check shown in the first screenshot.

Stop: `docker compose -f compose.es.yml --profile kibana down -v` and `docker compose -f compose.os.yml --profile dashboards down -v`. Remove the runner image with `docker rmi kb19-py`.
