You paste an Elasticsearch service into Compose, run docker compose up, and the container exits with code 78. The log says max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]. Or the node starts, your app still can't talk to it because localhost:9200 is the app's own loopback, or security returns 401 until you set ELASTIC_PASSWORD.
This guide reproduces those failures with docker.elastic.co/elasticsearch/elasticsearch:9.1.5 and a curlimages/curl:8.16.0 client on the same Compose network, then fixes them. Every error string below comes from that lab run on November 7, 2026. The Stack Overflow question Elasticsearch: Max virtual memory areas vm.max_map_count [65530] is too low had 402,986 views that day, How to increase vm.max_map_count? had 201,093, and Docker - ELK - vm.max_map_count had 72,355.
The short version:
- Raise
vm.max_map_counton the Docker host, not inside the container. Elastic's current docs recommend1048576. The bootstrap check text still says at least262144. - Production-style settings (
network.hostpluscluster.initial_master_nodes) enforce the check and exit 78.discovery.type=single-nodemay only WARN and still start. - From another container, use the Compose service name (
http://elasticsearch:9200), notlocalhost. - With security enabled (the 8.x and 9.x default), set
ELASTIC_PASSWORDand send Basic auth. - Persist the sysctl in
/etc/sysctl.d/so a reboot does not bring the failure back.
Table of contents
Fix vm.max_map_count bootstrap exit 78
Start with the map. Match the message you see to the cause and the fix:
Confirm the host value and the exact log line
The lab host started at the classic low default:
65530 With a production-style Compose file that binds a non-loopback address and sets cluster.initial_master_nodes, Elasticsearch enforced bootstrap checks and died:
services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:9.1.5 environment: - cluster.name=eslab - node.name=eslab-node - cluster.initial_master_nodes=eslab-node - xpack.security.enabled=false - ES_JAVA_OPTS=-Xms512m -Xmx512m - network.host=0.0.0.0 ports: - "127.0.0.1:9200:9200" The container status was Exited (78). The log named the check:
bound or publishing to a non-loopback address, enforcing bootstrap checks ERROR node validation exception [1] bootstrap checks failed. You must address the points described in the following [1] lines before starting Elasticsearch. bootstrap check failure [1] of [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] ERROR: Elasticsearch died while starting up, with exit code 78 Elasticsearch uses memory-mapped files for indices by default. The kernel limit vm.max_map_count caps how many mappings a process may hold. The Increase virtual memory docs say: "If the operating system's default vm.max_map_count value is 1048576 or higher, no configuration change is necessary. If the default value is lower than 1048576, configure the vm.max_map_count parameter to 1048576." The Docker production guide repeats that the setting "must be set to 1048576." The check message still quotes the older floor of 262144. Raise the host to 1048576 so you clear both the check and the current docs.
Raise it on the Docker host, then persist it
You cannot fix this from inside the container. The value lives on the host kernel. On Linux:
sudo sysctl -w vm.max_map_count=1048576 echo 'vm.max_map_count=1048576' | sudo tee /etc/sysctl.d/99-vm-max-map-count.conf sudo sysctl --system Verify with cat /proc/sys/vm/max_map_count or sysctl vm.max_map_count. After the lab raised the host to 1048576 and restarted the same Compose file, curl http://127.0.0.1:9200 returned the cluster root JSON with "number" : "9.1.5".
On Docker Desktop for Windows or Mac the knob sits in the Linux VM behind Docker, not in your Windows or macOS shell. Elastic's Docker production page documents wsl -d docker-desktop and .wslconfig paths for WSL2. Change it there, then restart the container.
single-node can WARN and still start
With discovery.type=single-node and the same low host value of 65530, the lab only logged a WARN and the node stayed up with HTTP 200:
WARN max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] That soft path is why some Compose snippets "work" on a laptop and then explode when you switch to production-style networking. Fix the host either way. Do not treat single-node as a substitute for sysctl.
Fix Connection refused to localhost:9200
Don't connect to localhost from the app container
Once Elasticsearch was healthy, a curl client on the same Compose network played the app. With http://127.0.0.1:9200/ it never reached Elasticsearch:
curl: (7) Failed to connect to 127.0.0.1 port 9200 after 0 ms: Could not connect to server The same client with http://elasticsearch:9200/ got the cluster root JSON. Each container has its own loopback interface, so localhost inside the app is the app. Elasticsearch listens in the elasticsearch service. The Redis guide and the RabbitMQ guide show the same mistake for those brokers.
From the Docker host itself you also need a published port. Without ports, a host tool cannot reach 9200. Add ports: ["127.0.0.1:9200:9200"] if a process on the host must talk HTTP. Other Compose services do not need that mapping.
Wait until the HTTP port is ready
depends_on: [elasticsearch] only waits for the container process to start. Right after a restart in the lab, an immediate curl http://elasticsearch:9200/ still failed to connect. After the healthcheck flipped to healthy, the same request returned 200. Use a healthcheck and condition: service_healthy, the pattern also used in the Umami guide with PostgreSQL:
services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:9.1.5 environment: - discovery.type=single-node - xpack.security.enabled=false - ES_JAVA_OPTS=-Xms512m -Xmx512m healthcheck: test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:9200 >/dev/null || exit 1"] interval: 5s timeout: 5s retries: 24 start_period: 25s app: image: your-app:tag depends_on: elasticsearch: condition: service_healthy Fix 401 missing authentication credentials
Elasticsearch 8.x and 9.x enable security by default. The lab turned it on with xpack.security.enabled=true, set ELASTIC_PASSWORD=LabPassw0rd!, and disabled HTTP TLS for a local Compose network with xpack.security.http.ssl.enabled=false.
Without credentials, curl got:
{"error":{"type":"security_exception","reason":"missing authentication credentials for REST request [/]"},"status":401} With a wrong password it got unable to authenticate user [elastic]. With -u elastic:LabPassw0rd! the root endpoint returned 200, a document indexed under lab-notes, and a search hit after _refresh. The same password pattern shows up for other databases in the PostgreSQL Docker auth guide and the MongoDB Docker auth guide.
Minimal secure Compose shape used in the lab:
services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:9.1.5 environment: - discovery.type=single-node - xpack.security.enabled=true - ELASTIC_PASSWORD=LabPassw0rd! - xpack.security.http.ssl.enabled=false - ES_JAVA_OPTS=-Xms512m -Xmx512m ports: - "127.0.0.1:9200:9200" healthcheck: test: ["CMD-SHELL", "curl -fsS -u elastic:LabPassw0rd! http://127.0.0.1:9200 >/dev/null || exit 1"] interval: 5s timeout: 5s retries: 30 start_period: 40s Put the password in an env file or a secret, not in a public git repo. For production TLS and enrollment tokens, follow Elastic's current Docker docs rather than disabling HTTP SSL.
Working Compose checklist
- Set host
vm.max_map_count=1048576and persist it under/etc/sysctl.d/. - Pin the image tag (
9.1.5in this lab, neverlatest). - Give the node enough heap for your box (
ES_JAVA_OPTS=-Xms512m -Xmx512mwas enough here). - Bind a data volume to
/usr/share/elasticsearch/data. - Point app containers at
http://elasticsearch:9200(or your service name) with credentials when security is on. - Gate app start on
condition: service_healthy. - Manage the stack from Portainer if you prefer a UI over the CLI.
The Grafana and Prometheus permission guide is another case where the Docker host, not the Compose YAML alone, owns the fix.
FAQ
What does vm.max_map_count control?
It is the Linux kernel limit on memory-mapped areas per process. Elasticsearch maps index files with mmap, so a low value fails bootstrap or risks mapping errors. See SO 42889241 and Elastic's virtual memory docs.
Why does the container exit with code 78?
Exit 78 is the lab result when bootstrap checks fail under enforced mode. The log line names vm.max_map_count. Fix the host and start again.
Does discovery.type=single-node remove the need for sysctl?
No. In the lab it only logged a WARN and still started. Production-style networking enforced the check and exited 78. Raise the host either way.
Can I set vm.max_map_count inside the Elasticsearch container?
No. Threads such as SO 41064572 hit a read-only /proc from inside the container. Change the Docker host (or the Docker Desktop VM).
Why does my app get connection refused on localhost:9200?
localhost inside the app container is that container. Use the Compose service name and wait for the healthcheck. Same pattern as Redis and RabbitMQ on this site.
How do I set the elastic user password in Compose?
Set ELASTIC_PASSWORD (and keep security enabled). Call the API with -u elastic:$ELASTIC_PASSWORD. Missing or wrong credentials return security_exception with status 401, as captured in the lab.







