Elasticsearch installation looks different depending on your OS, whether you're running it locally or in production, and how it fits into your existing stack. Docker gets you running in minutes, but package managers and native installs suit long-running servers better — and if you're mixing Elasticsearch with other containers, networking trips up almost everyone at least once. Below are the main installation paths for Elasticsearch 9, what each one assumes, and where they tend to go wrong.

Option 1: Docker (quickest, most consistent)

docker network create elastic
docker pull docker.elastic.co/elasticsearch/elasticsearch:9.0.0
docker run --name es01 --net elastic -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e "xpack.security.enabled=false" \
-it docker.elastic.co/elasticsearch/elasticsearch:9.0.0

Check the Elastic Docker Hub page for the current version tag before pulling — replace 9.0.0 accordingly.

If PHP is also running in a container, localhost:9200 from inside your PHP container won't reach Elasticsearch — that localhost refers to the PHP container itself, not the host or the Elasticsearch container. Instead:

Put both containers on the same Docker network (as above with --net elastic) and reference Elasticsearch by its container name, e.g. http://es01:9200, from your PHP code.

If you're using Docker Compose, this is simpler — services on the same Compose network can reach each other by service name automatically:

services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:9.0.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- "9200:9200"
php:
build: .
depends_on:
- elasticsearch

Then in PHP, connect to http://elasticsearch:9200 (the service name), not localhost:9200.

Option 2: Debian/Ubuntu (APT)

wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] https://artifacts.elastic.co/packages/9.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-9.x.list
sudo apt update
sudo apt install elasticsearch
sudo systemctl enable elasticsearch
sudo systemctl start elasticsearch

Option 3: RHEL/CentOS (YUM)

sudo rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch

Add a repo file at /etc/yum.repos.d/elasticsearch.repo:

[elasticsearch]
name=Elasticsearch repository
baseurl=https://artifacts.elastic.co/packages/9.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1

Then:

sudo yum install elasticsearch
sudo systemctl enable elasticsearch
sudo systemctl start elasticsearch

Option 4: Homebrew (macOS)

brew tap elastic/tap
brew install elastic/tap/elasticsearch-full
brew services start elastic/tap/elasticsearch-full

A couple of things worth knowing here:

  • Skip the plain brew install elasticsearch from Homebrew core — it can be an older or incomplete build. The elastic/tap/elasticsearch-full formula is the one Elastic actually maintains, so use that.
  • Even Elastic's own tap can lag a version or two behind the latest release. If you need a specific point release, check brew info elastic/tap/elasticsearch-full against the Elastic downloads page before relying on it.
  • Config and data live under /usr/local/etc/elasticsearch (Intel Macs) or /opt/homebrew/etc/elasticsearch (Apple Silicon).

Option 5: Manual tarball (any Linux/macOS)

curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-9.0.0-linux-x86_64.tar.gz
tar -xzf elasticsearch-9.0.0-linux-x86_64.tar.gz
cd elasticsearch-9.0.0/
./bin/elasticsearch

After install

  • Test with: curl -X GET "https://localhost:9200" — security (TLS + auth) is enabled by default. On first start you'll get an auto-generated password for the elastic user and an enrollment token, printed to the terminal.
  • Elasticsearch 9 requires Java 17 minimum but ships with a bundled JVM, so a separate install usually isn't needed.
  • Watch memory: by default it'll try to grab a large chunk of RAM; set -Xms/-Xmx in jvm.options for production.