Elasticsearch ships official Docker images at docker.elastic.co/elasticsearch/elasticsearch. The image is the fastest way to spin up a development cluster, run integration tests, or evaluate a new version. Production Docker deployments are viable but require attention to memory locking, file descriptor limits, persistent storage, and Elasticsearch's 8.x security defaults - which differ noticeably from 7.x.
Prerequisites
Docker Engine 20.10 or later
At least 2 GB of RAM available to the container (4 GB+ recommended)
Linux host with
vm.max_map_countset to at least 262144:sudo sysctl -w vm.max_map_count=262144Add
vm.max_map_count=262144to/etc/sysctl.confto persist across reboots.
Step 1: Pull the Official Image
Pin a specific version - never use latest in production:
docker pull docker.elastic.co/elasticsearch/elasticsearch:8.15.3
The 9.x line is current as of mid-2026; pick the version matching your stack components (Kibana, Logstash, Beats, clients).
Step 2: Run a Single-Node Container
A development-friendly command with security disabled:
docker run -d --name elasticsearch \
-p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e "xpack.security.enabled=false" \
-e "ES_JAVA_OPTS=-Xms1g -Xmx1g" \
docker.elastic.co/elasticsearch/elasticsearch:8.15.3
Key flags:
| Flag | Purpose |
|---|---|
-p 9200:9200 |
REST API |
-p 9300:9300 |
Transport (only needed for multi-node) |
discovery.type=single-node |
Skip cluster bootstrapping |
xpack.security.enabled=false |
Local development only - never in production |
ES_JAVA_OPTS |
Heap size, set -Xms == -Xmx |
For production-style setup with security enabled (the 8.x default), omit xpack.security.enabled=false and let Elasticsearch auto-generate the elastic user password on first start. Capture it from the container logs:
docker logs elasticsearch | grep -i "Password for the elastic user"
Step 3: Verify
curl http://localhost:9200/
With security enabled:
curl -u elastic:<password> http://localhost:9200/
The response is a JSON document with the cluster name, version, and Lucene version.
Step 4: Docker Compose Setup
A reproducible single-node config with persistent storage and memory locking:
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.15.3
container_name: elasticsearch
environment:
- discovery.type=single-node
- bootstrap.memory_lock=true
- ES_JAVA_OPTS=-Xms2g -Xmx2g
- xpack.security.enabled=true
- ELASTIC_PASSWORD=changeme
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
ports:
- "9200:9200"
volumes:
- esdata:/usr/share/elasticsearch/data
volumes:
esdata:
driver: local
Start it:
docker compose up -d
Step 5: Multi-Node Cluster
A three-node cluster with discovery.seed_hosts and cluster.initial_master_nodes:
services:
es01:
image: docker.elastic.co/elasticsearch/elasticsearch:8.15.3
environment:
- node.name=es01
- cluster.name=demo-cluster
- discovery.seed_hosts=es02,es03
- cluster.initial_master_nodes=es01,es02,es03
- bootstrap.memory_lock=true
- ES_JAVA_OPTS=-Xms2g -Xmx2g
ulimits:
memlock: { soft: -1, hard: -1 }
volumes:
- esdata01:/usr/share/elasticsearch/data
ports: ["9200:9200"]
es02:
image: docker.elastic.co/elasticsearch/elasticsearch:8.15.3
environment:
- node.name=es02
- cluster.name=demo-cluster
- discovery.seed_hosts=es01,es03
- cluster.initial_master_nodes=es01,es02,es03
- ES_JAVA_OPTS=-Xms2g -Xmx2g
ulimits:
memlock: { soft: -1, hard: -1 }
volumes:
- esdata02:/usr/share/elasticsearch/data
es03:
image: docker.elastic.co/elasticsearch/elasticsearch:8.15.3
environment:
- node.name=es03
- cluster.name=demo-cluster
- discovery.seed_hosts=es01,es02
- cluster.initial_master_nodes=es01,es02,es03
- ES_JAVA_OPTS=-Xms2g -Xmx2g
ulimits:
memlock: { soft: -1, hard: -1 }
volumes:
- esdata03:/usr/share/elasticsearch/data
volumes: { esdata01: , esdata02: , esdata03: }
For real production, enable TLS for the transport layer and HTTP - the 8.x image generates self-signed certs automatically on first start if security is enabled, or you can mount certs into /usr/share/elasticsearch/config/certs.
Production Considerations
| Concern | Recommendation |
|---|---|
| Storage | Use named volumes or bind mounts; never rely on container layer storage |
| Memory locking | bootstrap.memory_lock=true plus ulimits.memlock: -1 |
| Heap size | -Xms == -Xmx, under 50% of container memory, no more than ~30 GB |
| File descriptors | Set ulimits.nofile to 65536+ |
| Security | Enabled by default in 8.x - keep it that way |
| Networking | Use a dedicated Docker network; don't expose port 9300 to the internet |
| Image pinning | Pin to a specific version, never latest |
| Health checks | Configure HEALTHCHECK on the container |
Common Pitfalls
- Running without
vm.max_map_countraised. The container starts but fails to mmap segments, hitting bizarre errors at runtime. - Setting
discovery.type=single-nodeon what's supposed to be a multi-node cluster. The node ignoresdiscovery.seed_hosts. - Mounting a non-empty
datadirectory from a different cluster's volume. The node starts but joins the wrong cluster or fails on UUID mismatch. - Letting the container OOM-killer kill Elasticsearch. The default container memory limit isn't aware of the JVM heap - set a Docker memory limit that exceeds heap by 1-2 GB.
- Forgetting to copy the auto-generated 8.x security credentials before recreating the container. The credentials are stored in the data volume but printed only on first start.
Operating Elasticsearch in Docker
Pulse connects to Elasticsearch running in Docker, Docker Compose, or Kubernetes the same way it connects to bare-metal clusters - via the REST API. Configuration drift between containers, missing memlock, undersized heaps, and discovery misconfigurations are surfaced automatically, so the operational cost of running Elasticsearch in containers stays manageable as the stack grows.
Frequently Asked Questions
Q: How do I run Elasticsearch in Docker?
A: docker run -d --name elasticsearch -p 9200:9200 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:<version>. The 8.x image enables security by default and prints the elastic user password on first start.
Q: What's the minimum memory for Elasticsearch in Docker?
A: 2 GB total container memory is the minimum to start; 4 GB is more practical. Set ES_JAVA_OPTS to -Xms1g -Xmx1g (or higher) - heap should be at most 50% of container memory.
Q: How do I get the elastic user password in Docker 8.x?
A: On first start, Elasticsearch logs the auto-generated password: docker logs elasticsearch | grep "Password for the elastic user". To reset, run docker exec -it elasticsearch bin/elasticsearch-reset-password -u elastic.
Q: Can I run a multi-node Elasticsearch cluster in Docker?
A: Yes - use Docker Compose with each node defined as a service, set cluster.initial_master_nodes and discovery.seed_hosts, and give each node its own data volume. Avoid discovery.type=single-node, which disables cluster bootstrapping.
Q: Why does my Elasticsearch container fail with bootstrap check errors?
A: Most often vm.max_map_count is below 262144 on the host. Run sudo sysctl -w vm.max_map_count=262144. Other causes: memlock ulimit not raised, file descriptors below 65535, heap settings inconsistent.
Q: Is it safe to run Elasticsearch in Docker in production?
A: Yes, but pay attention to persistent storage (named volumes), ulimits (memlock, nofile), heap sizing, security (keep it enabled), and version pinning. The container runtime adds no real overhead; misconfiguration is the usual source of trouble.
Related Reading
- Where to Download Elasticsearch: All installation options
- Elasticsearch bootstrap.memory_lock Setting: Lock heap in RAM
- Elasticsearch Heap Size Setting: Heap sizing guidance
- Elasticsearch Disable Security: Local development setup
- Elasticsearch transport.publish_host Setting: Container networking
- Elasticsearch Documentation: Full Elasticsearch knowledge base