A single-node Elasticsearch in Docker is the standard dev setup: one container, dev-mode discovery, modest JVM heap, persistent volume. The image is from docker.elastic.co, not Docker Hub, and there are three things that catch first-time users — a Linux kernel setting, the JVM heap default, and the security defaults that turned on in 8.0.
How do I run Elasticsearch in Docker?
docker run -d --name es \
-e discovery.type=single-node \
-e xpack.security.enabled=false \
-e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
-p 9200:9200 \
-v es-data:/usr/share/elasticsearch/data \
docker.elastic.co/elasticsearch/elasticsearch:8.16.0Single-node mode, security disabled (dev only), 512 MB JVM heap, port 9200 published, data volume mounted. Test it:
curl http://127.0.0.1:9200The cluster info JSON comes back.
Configure version, ports, heap, and volume.
On Linux: fix vm.max_map_count first
Elasticsearch uses memory-mapped files heavily and needs a higher vm.max_map_count than the Linux default. Without this, the container starts but logs an error and refuses to serve requests.
sudo sysctl -w vm.max_map_count=262144Make it persistent across reboots:
echo 'vm.max_map_count=262144' | sudo tee /etc/sysctl.d/99-elasticsearch.confOn Docker Desktop (Mac and Windows), this is handled inside Docker's VM — you don't need to set it on the host.
The basic run command
docker run -d --name :container_name
-e discovery.type=single-node
-e xpack.security.enabled=false
-e ES_JAVA_OPTS="-Xms:heap -Xmx:heap"
-p :host_port:9200
-v :volume:/usr/share/elasticsearch/data
docker.elastic.co/elasticsearch/elasticsearch::es_versionThree env vars to know:
discovery.type=single-node— tells ES not to look for cluster peers. Required for one-container setups.xpack.security.enabled=false— disables the auth/TLS features turned on by default in 8.0+. For dev only. With it enabled, the first start prints a generatedelasticuser password and you need it to connect; convenient for production-shaped local setups, friction for quick experiments.ES_JAVA_OPTS="-Xms512m -Xmx512m"— sets the JVM heap (initial = max = 512 MB). The image's default heap can easily eat 1-2 GB on a host that didn't ask for it; pin the size.
Why mount /usr/share/elasticsearch/data
ES stores all indices, shard data, and the cluster state under /usr/share/elasticsearch/data inside the container. Same pattern as the other databases — without a volume, that data is in the container's writable layer and dies with docker rm.
docker stop :container_name && docker rm :container_namedocker run -d --name :container_name
-e discovery.type=single-node
-e xpack.security.enabled=false
-e ES_JAVA_OPTS="-Xms:heap -Xmx:heap"
-p :host_port:9200
-v :volume:/usr/share/elasticsearch/data
docker.elastic.co/elasticsearch/elasticsearch::es_versionIndices are still there.Practical usage: connecting and indexing
Health check:
curl http://127.0.0.1::host_port/_cluster/health?prettyCreate an index, post a document, search:
curl -X POST http://127.0.0.1::host_port/articles/_doc -H 'Content-Type: application/json' -d '{
"title": "Hello Elasticsearch",
"body": "First document"
}'
curl 'http://127.0.0.1::host_port/articles/_search?pretty&q=Hello'Pair with Kibana for a UI:
services:
es:
image: docker.elastic.co/elasticsearch/elasticsearch::es_version
environment:
discovery.type: single-node
xpack.security.enabled: "false"
ES_JAVA_OPTS: "-Xms:heap -Xmx:heap"
volumes:
:volume:/usr/share/elasticsearch/data
ports:
"::host_port:9200"
kibana:
image: docker.elastic.co/kibana/kibana::es_version
depends_on:
es
ports:
"5601:5601"
volumes:
:volume:Kibana on http://127.0.0.1:5601 after both containers start.
Common pitfalls
vm.max_map_count too low— set it (see Linux section above). Docker Desktop handles this; bare-metal Linux does not.- OOM kills. Set
ES_JAVA_OPTS="-Xms512m -Xmx512m"(or larger for real workloads). Without the cap, the JVM can grab gigabytes. - Security blocking connections in 8.x. Either set
xpack.security.enabled=falsefor dev, or accept the generated password from the first-start logs. - Publishing 9200 to the whole internet.
-p 9200:9200binds to0.0.0.0, every interface, and Docker can punch that past your host firewall. On a box with a public IP, an unauthenticated node is then reachable from the internet and gets found and wiped by automated bots within hours. Bind to127.0.0.1:9200:9200for anything that is not purely local. I learned this the expensive way: how an open Elasticsearch port got my database ransomed. - Pulling from Docker Hub instead of
docker.elastic.co. The Hub still has an oldelasticsearchmirror; the current ES is only ondocker.elastic.co. Same with Kibana. - Apple Silicon and older ES. The arm64 image arrived in 7.17+; pre-7.17 ES on Apple Silicon needs
--platform linux/amd64and emulation.
What to do next
- How to Use ElasticPress with WP_Query — pair this with WordPress.
- Elasticsearch Cheat Sheet — once it's running, the query-side reference.
- How to reindex Elasticsearch with zero downtime: when you need to change a mapping, the alias-and-reindex dance that avoids dropping and recreating the index.
- Elasticsearch vs OpenSearch for ElasticPress — if you were considering OpenSearch instead.
- Elasticsearch ransomware: how an open port wiped my database — before you publish 9200, read what happens when it is reachable from the internet.
FAQ
Only with a volume on /usr/share/elasticsearch/data. Without it, the indices die with the container.
Almost always one of three things: vm.max_map_count too low (Linux), the JVM running out of memory (set ES_JAVA_OPTS), or the security bootstrap on 8.x failing because the host can't write to the certificate directory. Check docker logs :container_name — it tells you which.
docker.elastic.co is Elastic's official registry and has the current images. The Docker Hub elasticsearch namespace was deprecated years ago and only has old versions. Always pull from docker.elastic.co for 7.x and 8.x.
For a dev single-node, 1-2 GB is fine (set -Xms512m -Xmx512m). For production, the rule of thumb is half the host's RAM for the JVM heap, capped at 30 GB. Single-node dev containers don't need that.
Yes for many use cases — OpenSearch is the Apache-2.0 fork. The Docker run pattern is the same, just opensearchproject/opensearch as the image and slightly different env var names. For ElasticPress (the WordPress integration), check Elasticsearch vs OpenSearch for ElasticPress — that one is more nuanced.
Sources
Authoritative references this article was fact-checked against.
- Elastic's official Docker registrydocker.elastic.co
- Elasticsearch Docker — official guideelastic.co





