$linuxjunkies
>

Install Elasticsearch or OpenSearch

Install Elasticsearch 8 or OpenSearch 2 on Linux, tune JVM heap, configure security, set up single-node or cluster mode, and verify with live index operations.

IntermediateUbuntuDebianFedoraArch10 min readUpdated June 7, 2026

Before you start

  • A server with at least 4 GB RAM (8 GB+ recommended for production)
  • sudo or root access
  • Outbound HTTPS access to download packages and GPG keys
  • Basic familiarity with systemd service management and curl

Elasticsearch and OpenSearch are both Lucene-based distributed search and analytics engines. OpenSearch is the AWS-maintained Apache 2.0 fork of Elasticsearch 7.10, created after Elastic moved to the SSPL license. For most new deployments, OpenSearch is the safer open-source choice; Elasticsearch remains the right pick if you need X-Pack features or Elastic Cloud integration. This guide covers installing either engine on a single node, sizing the JVM correctly, locking down security, and verifying the cluster with real index operations.

Choose Your Engine

The installation paths are similar but not identical. Decide before you start:

  • Elasticsearch 8.x — security enabled by default, requires enrollment tokens for cluster formation.
  • OpenSearch 2.x — security plugin ships enabled, uses its own demo certificates out of the box.

Both require a modern JVM, but they bundle their own JDK — you do not need to install Java separately.

System Prerequisites

Both engines are memory-hungry and require OS-level tuning. Do this on every node before installing anything.

Set vm.max_map_count

The default kernel limit on memory-mapped areas is too low for Lucene.

sudo sysctl -w vm.max_map_count=262144

Make it permanent:

echo 'vm.max_map_count=262144' | sudo tee /etc/sysctl.d/99-search.conf
sudo sysctl --system

Raise the open-file limit

sudo tee /etc/security/limits.d/search.conf <<'EOF'
elasticsearch  -  nofile  65535
opensearch     -  nofile  65535
EOF

Disable swap

Both engines will log warnings and perform poorly if memory is swapped out.

sudo swapoff -a
# Comment out swap lines in /etc/fstab to survive reboots

JVM Heap Sizing

This is the single most impactful tuning decision. Rules that apply to both engines:

  • Set -Xms and -Xmx to the same value to avoid heap resizing at runtime.
  • Do not exceed 50 % of total RAM — the other half is needed by the OS page cache for Lucene file access.
  • Do not go above 31 GB. Above this threshold the JVM can no longer use compressed ordinary object pointers (compressed oops), which wastes memory and hurts GC performance.
  • Minimum practical heap for a test node: 1 GB. Minimum for production: 4–8 GB.

Edit the JVM options file after installation (paths shown below). Both engines use a jvm.options.d/ drop-in directory — prefer that over editing the main file.

# Elasticsearch
echo '-Xms4g
-Xmx4g' | sudo tee /etc/elasticsearch/jvm.options.d/heap.options

# OpenSearch
echo '-Xms4g
-Xmx4g' | sudo tee /etc/opensearch/jvm.options.d/heap.options

Install Elasticsearch 8.x

Debian / Ubuntu

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/8.x/apt stable main" \
  | sudo tee /etc/apt/sources.list.d/elasticsearch.list

sudo apt update && sudo apt install -y elasticsearch

Fedora / RHEL / Rocky

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

sudo tee /etc/yum.repos.d/elasticsearch.repo <<'EOF'
[elasticsearch]
name=Elasticsearch repository for 8.x packages
baseurl=https://artifacts.elastic.co/packages/8.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md
EOF

sudo dnf install -y elasticsearch

Enable and start

sudo systemctl daemon-reload
sudo systemctl enable --now elasticsearch

On first start Elasticsearch 8.x prints an auto-generated elastic user password and an enrollment token to the journal. Capture them immediately:

sudo journalctl -u elasticsearch --no-pager | grep -E 'password|enrollment'

Install OpenSearch 2.x

Debian / Ubuntu

curl -o- https://artifacts.opensearch.org/publickeys/opensearch.pgp \
  | sudo gpg --dearmor -o /usr/share/keyrings/opensearch-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/opensearch-keyring.gpg] \
https://artifacts.opensearch.org/packages/2.x/apt stable main" \
  | sudo tee /etc/apt/sources.list.d/opensearch.list

sudo apt update && sudo apt install -y opensearch

Fedora / RHEL / Rocky

sudo rpm --import https://artifacts.opensearch.org/publickeys/opensearch.pgp

sudo tee /etc/yum.repos.d/opensearch.repo <<'EOF'
[opensearch-2.x]
name=OpenSearch 2.x repository
baseurl=https://artifacts.opensearch.org/packages/2.x/yum
gpgcheck=1
gpgkey=https://artifacts.opensearch.org/publickeys/opensearch.pgp
enabled=1
autorefresh=1
EOF

sudo dnf install -y opensearch

Enable and start

sudo systemctl daemon-reload
sudo systemctl enable --now opensearch

Security Configuration

Elasticsearch 8.x

Security is on by default with TLS on the transport and HTTP layers. For a single-node lab, the simplest path is to reset the built-in password after verifying the node is up:

sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic -i

The self-signed CA certificate lives at /etc/elasticsearch/certs/http_ca.crt. Pass it to curl with --cacert or copy it to clients.

OpenSearch 2.x

The security plugin runs with auto-generated demo certificates on install. For any non-throwaway environment, replace them. At minimum, run the demo configuration script and then change default passwords:

sudo /usr/share/opensearch/plugins/opensearch-security/tools/install_demo_configuration.sh -y -i -s
sudo systemctl restart opensearch

Change the admin password via the security REST API or the securityadmin.sh tool once the node is healthy.

Single-Node vs. Cluster Configuration

For a single node, add these lines to the main config file to prevent the node from waiting for peers indefinitely:

# /etc/elasticsearch/elasticsearch.yml  OR  /etc/opensearch/opensearch.yml
discovery.type: single-node
cluster.name: my-cluster
node.name: node-1
network.host: 127.0.0.1   # change to 0.0.0.0 only when firewalled

For a multi-node cluster, set discovery.seed_hosts and cluster.initial_master_nodes (Elasticsearch) or cluster.initial_cluster_manager_nodes (OpenSearch 2.x, which renamed the role) to the IP addresses and names of your seed nodes. Keep an odd number of master-eligible nodes (3, 5) to avoid split-brain.

Firewall Rules

# ufw (Ubuntu/Debian)
sudo ufw allow from 10.0.0.0/24 to any port 9200
sudo ufw allow from 10.0.0.0/24 to any port 9300  # transport layer

# firewalld (Fedora/RHEL)
sudo firewall-cmd --add-rich-rule='rule family=ipv4 source address=10.0.0.0/24 port port=9200 protocol=tcp accept' --permanent
sudo firewall-cmd --add-rich-rule='rule family=ipv4 source address=10.0.0.0/24 port port=9300 protocol=tcp accept' --permanent
sudo firewall-cmd --reload

Never expose port 9200 directly to the internet without authentication and TLS.

Verify: Cluster Health and Basic Indexing

Check cluster health

# Elasticsearch (replace PASSWORD and path to CA cert as needed)
curl -u elastic:PASSWORD --cacert /etc/elasticsearch/certs/http_ca.crt \
  https://localhost:9200/_cluster/health?pretty

# OpenSearch (demo certs, for testing only)
curl -ku admin:admin https://localhost:9200/_cluster/health?pretty

Expect "status": "green" on a single node with no indices, or "yellow" once you have indices with replicas that cannot be assigned.

Create an index and ingest a document

curl -ku admin:admin -X PUT https://localhost:9200/books \
  -H 'Content-Type: application/json' \
  -d '{"settings":{"number_of_shards":1,"number_of_replicas":0}}'

curl -ku admin:admin -X POST https://localhost:9200/books/_doc \
  -H 'Content-Type: application/json' \
  -d '{"title":"The Linux Command Line","author":"William Shotts","year":2019}'
curl -ku admin:admin -X GET https://localhost:9200/books/_search?pretty \
  -H 'Content-Type: application/json' \
  -d '{"query":{"match":{"title":"linux"}}}'

Troubleshooting

  • Node won't start, exits immediately — check sudo journalctl -u elasticsearch -n 60. The most common causes are vm.max_map_count too low or heap set larger than available RAM.
  • Status stays red — run /_cluster/allocation/explain?pretty to see why shards are unassigned. On a single node, set number_of_replicas: 0 on all indices.
  • Authentication errors on OpenSearch — after changing config or certificates, the security index may need to be re-initialized with securityadmin.sh.
  • High GC pressure / OutOfMemoryError — heap is either too small or above the 31 GB compressed-oops threshold. Reduce or cap it at 30 GB.
  • Port 9200 connection refused — confirm network.host is set and the service is running: sudo systemctl status elasticsearch.
tested on:Ubuntu 24.04Debian 12Rocky 9Fedora 40

Frequently asked questions

Do I need to install Java before installing Elasticsearch or OpenSearch?
No. Both packages bundle a compatible JDK. Installing a system JVM separately is unnecessary and can cause conflicts if JAVA_HOME is set to the wrong version.
Why does my single-node cluster show yellow instead of green health?
Yellow means primary shards are assigned but replica shards are not. On a single node there is nowhere to place replicas. Set number_of_replicas to 0 on your indices and the status will turn green.
What is the difference between Elasticsearch 8 and OpenSearch 2 security models?
Elasticsearch 8 enables TLS and authentication by default and generates certificates automatically on first start. OpenSearch 2 ships the security plugin enabled with demo self-signed certificates that must be replaced for production use.
How many master-eligible nodes should a production cluster have?
Always an odd number — 3 is the standard minimum. This ensures a quorum can be reached and prevents split-brain if one node loses network connectivity.
Can I run both Elasticsearch and OpenSearch on the same server?
Technically yes, but they will conflict on ports 9200 and 9300. You would need to change the port bindings for one of them in its YAML config, and the JVM memory demands make co-hosting on anything under 16 GB RAM impractical.

Related guides