DEV Community

DeployLark
DeployLark

Posted on Originally published at deploylark.com

Elasticsearch in Docker: Fix vm.max_map_count Too Low

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_count on the Docker host, not inside the container. Elastic's current docs recommend 1048576. The bootstrap check text still says at least 262144.
  • Production-style settings (network.host plus cluster.initial_master_nodes) enforce the check and exit 78. discovery.type=single-node may only WARN and still start.
  • From another container, use the Compose service name (http://elasticsearch:9200), not localhost.
  • With security enabled (the 8.x and 9.x default), set ELASTIC_PASSWORD and 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:

Table diagram mapping Elasticsearch Docker errors to causes and fixes: vm.max_map_count bootstrap exit 78, localhost connection refused on 9200, and security_exception 401

Confirm the host value and the exact log line

The lab host started at the classic low default:

65530
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Terminal capture: Elasticsearch container exits with code 78 and logs that vm.max_map_count 65530 is too low while enforcing bootstrap checks

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
Enter fullscreen mode Exit fullscreen mode

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".

Terminal capture: sysctl raises vm.max_map_count to 1048576, then curl to port 9200 returns Elasticsearch 9.1.5 cluster JSON

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]
Enter fullscreen mode Exit fullscreen mode

Terminal capture: discovery.type single-node only warns that vm.max_map_count is too low while curl still returns HTTP 200

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
Enter fullscreen mode Exit fullscreen mode

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.

Terminal capture: curl from an app container fails on 127.0.0.1:9200 then succeeds on http://elasticsearch:9200

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
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

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.

Terminal capture: Elasticsearch returns 401 without credentials or with a wrong password, then succeeds with elastic user and ELASTIC_PASSWORD

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
Enter fullscreen mode Exit fullscreen mode

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

  1. Set host vm.max_map_count=1048576 and persist it under /etc/sysctl.d/.
  2. Pin the image tag (9.1.5 in this lab, never latest).
  3. Give the node enough heap for your box (ES_JAVA_OPTS=-Xms512m -Xmx512m was enough here).
  4. Bind a data volume to /usr/share/elasticsearch/data.
  5. Point app containers at http://elasticsearch:9200 (or your service name) with credentials when security is on.
  6. Gate app start on condition: service_healthy.
  7. 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.

Top comments (0)