OpenSearch Installation and Configuration

OpenSearch is an Apache-licensed fork of Elasticsearch that provides full-text search, analytics, and observability capabilities without vendor lock-in. This guide covers deploying OpenSearch on Linux, setting up a cluster, managing indexes, configuring OpenSearch Dashboards, enabling the security plugin, and migrating from Elasticsearch.

Prerequisites

  • Ubuntu 20.04+ or CentOS 8+ / Rocky Linux 8+
  • Java 11+ (OpenSearch bundles its own JDK)
  • 4 GB RAM minimum per node (8–16 GB for production)
  • SSD storage for optimal index performance
  • Root or sudo access

Installing OpenSearch

# Ubuntu/Debian
curl -o- https://artifacts.opensearch.org/publickeys/opensearch.pgp | sudo gpg --dearmor --batch --yes -o /usr/share/keyrings/opensearch-keyring.gpg

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

sudo apt-get update
# Set the initial admin password (required for OpenSearch 2.12+)
sudo OPENSEARCH_INITIAL_ADMIN_PASSWORD='YourStr0ngP@ssword!' apt-get install -y opensearch

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

cat > /etc/yum.repos.d/opensearch-2.x.repo << 'EOF'
[opensearch-2.x-release]
name=OpenSearch 2.x Release
baseurl=https://artifacts.opensearch.org/releases/bundle/opensearch/2.x/yum
autorefresh=1
type=rpm-md
gpgcheck=1
gpgkey=https://artifacts.opensearch.org/publickeys/opensearch.pgp
priority=1
enabled=1
EOF

sudo OPENSEARCH_INITIAL_ADMIN_PASSWORD='YourStr0ngP@ssword!' dnf install -y opensearch

Configuring OpenSearch

# Edit the main config
sudo nano /etc/opensearch/opensearch.yml

Key settings for a single-node development setup:

# /etc/opensearch/opensearch.yml

cluster.name: my-opensearch-cluster
node.name: node-1

# Data and logs paths
path.data: /var/lib/opensearch
path.logs: /var/log/opensearch

# Network
network.host: 0.0.0.0
http.port: 9200

# Single-node discovery (dev only)
discovery.type: single-node

# Disable security for development (NOT for production)
plugins.security.disabled: true

Tune JVM memory (set to ~50% of available RAM, max 32 GB):

sudo nano /etc/opensearch/jvm.options
# Change:
-Xms4g
-Xmx4g

Set OS limits for OpenSearch:

# Increase virtual memory limit
sudo sysctl -w vm.max_map_count=262144
echo 'vm.max_map_count=262144' | sudo tee /etc/sysctl.d/99-opensearch.conf

# Increase file descriptor limits
cat >> /etc/security/limits.conf << 'EOF'
opensearch soft nofile 65536
opensearch hard nofile 65536
EOF

# Enable and start
sudo systemctl enable --now opensearch
sudo systemctl status opensearch

# Test
curl http://localhost:9200

Cluster Setup

For a 3-node production cluster, configure each node:

Node 1 (master + data):

# /etc/opensearch/opensearch.yml on node-1

cluster.name: prod-cluster
node.name: node-1
node.roles: [master, data, ingest]

network.host: 192.168.1.10
http.port: 9200
transport.port: 9300

# Cluster formation
cluster.initial_master_nodes:
  - node-1
  - node-2
  - node-3

discovery.seed_hosts:
  - 192.168.1.10:9300
  - 192.168.1.11:9300
  - 192.168.1.12:9300

Node 2 and 3 - same config but change node.name and network.host.

For dedicated master nodes (clusters > 10 nodes):

node.roles: [master]   # No data role = master-eligible only

Index Management

# Create an index with settings
curl -X PUT http://localhost:9200/logs-2024 \
  -H 'Content-Type: application/json' \
  -d '{
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "index.refresh_interval": "5s"
    },
    "mappings": {
      "properties": {
        "timestamp":  {"type": "date"},
        "level":      {"type": "keyword"},
        "message":    {"type": "text", "analyzer": "standard"},
        "service":    {"type": "keyword"},
        "duration_ms":{"type": "float"}
      }
    }
  }'

# Index a document
curl -X POST http://localhost:9200/logs-2024/_doc \
  -H 'Content-Type: application/json' \
  -d '{"timestamp": "2024-01-15T12:00:00Z", "level": "ERROR", "message": "Connection refused", "service": "api"}'

# Search
curl -X GET http://localhost:9200/logs-2024/_search \
  -H 'Content-Type: application/json' \
  -d '{
    "query": {
      "bool": {
        "must": [{"match": {"message": "connection"}}],
        "filter": [
          {"term": {"level": "ERROR"}},
          {"range": {"timestamp": {"gte": "now-1d"}}}
        ]
      }
    },
    "sort": [{"timestamp": "desc"}],
    "size": 20
  }'

# Create an index template for rolling logs
curl -X PUT http://localhost:9200/_index_template/logs-template \
  -H 'Content-Type: application/json' \
  -d '{
    "index_patterns": ["logs-*"],
    "template": {
      "settings": {"number_of_shards": 2, "number_of_replicas": 1},
      "mappings": {
        "properties": {
          "timestamp": {"type": "date"},
          "level":     {"type": "keyword"},
          "message":   {"type": "text"}
        }
      }
    }
  }'

Index State Management (ISM) - auto-rollover and delete old indexes:

curl -X PUT http://localhost:9200/_plugins/_ism/policies/logs-policy \
  -H 'Content-Type: application/json' \
  -d '{
    "policy": {
      "description": "Rollover after 50GB or 30 days, delete after 90 days",
      "default_state": "hot",
      "states": [
        {
          "name": "hot",
          "actions": [{"rollover": {"min_size": "50gb", "min_index_age": "30d"}}],
          "transitions": [{"state_name": "delete", "conditions": {"min_index_age": "90d"}}]
        },
        {
          "name": "delete",
          "actions": [{"delete": {}}],
          "transitions": []
        }
      ]
    }
  }'

Installing OpenSearch Dashboards

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

sudo apt-get update && sudo apt-get install -y opensearch-dashboards

# Configure
sudo nano /etc/opensearch-dashboards/opensearch_dashboards.yml
server.host: "0.0.0.0"
server.port: 5601
opensearch.hosts: ["https://localhost:9200"]
opensearch.username: "kibanaserver"
opensearch.password: "kibanaserver"
opensearch.ssl.verificationMode: none
sudo systemctl enable --now opensearch-dashboards
# Access at http://your-server:5601

Security Plugin Configuration

The security plugin is included by default. Initialize it:

# Run the security admin script after first start
cd /usr/share/opensearch/plugins/opensearch-security/tools

OPENSEARCH_JAVA_HOME=/usr/share/opensearch/jdk \
./securityadmin.sh \
  -cd /etc/opensearch/opensearch-security/ \
  -icl -nhnv \
  -cacert /etc/opensearch/root-ca.pem \
  -cert /etc/opensearch/kirk.pem \
  -key /etc/opensearch/kirk-key.pem

Add a user in internal_users.yml:

# Hash the password
/usr/share/opensearch/plugins/opensearch-security/tools/hash.sh -p mypassword

# Edit internal_users.yml and add:
myuser:
  hash: "$2y$12$hashedpassword..."
  backend_roles:
    - "readall"

Alerting and Anomaly Detection

Create an alert monitor via the API:

curl -X POST http://localhost:9200/_plugins/_alerting/monitors \
  -H 'Content-Type: application/json' \
  -d '{
    "type": "monitor",
    "name": "High Error Rate",
    "enabled": true,
    "schedule": {"period": {"interval": 5, "unit": "MINUTES"}},
    "inputs": [{
      "search": {
        "indices": ["logs-*"],
        "query": {
          "size": 0,
          "query": {
            "bool": {
              "filter": [
                {"term": {"level": "ERROR"}},
                {"range": {"timestamp": {"gte": "now-5m"}}}
              ]
            }
          },
          "aggs": {"error_count": {"value_count": {"field": "level"}}}
        }
      }
    }],
    "triggers": [{
      "name": "Error spike",
      "severity": "1",
      "condition": {"script": {"source": "ctx.results[0].aggregations.error_count.value > 100"}}
    }]
  }'

Troubleshooting

OpenSearch fails to start:

sudo journalctl -u opensearch -f
# Common: vm.max_map_count too low, or JVM heap too large
sudo sysctl vm.max_map_count

Cluster health is yellow or red:

curl http://localhost:9200/_cluster/health?pretty
# Yellow = unassigned replica shards (OK for single-node)
# Red = missing primary shards

# Check unassigned shards
curl http://localhost:9200/_cluster/allocation/explain?pretty

High disk usage:

curl http://localhost:9200/_cat/indices?v&s=store.size:desc
# Delete old indexes or enable ISM policy

Out of memory errors:

# Check JVM heap usage
curl http://localhost:9200/_nodes/stats/jvm?pretty | grep heap_used_percent

Conclusion

OpenSearch is a production-ready search and analytics platform that works as a drop-in replacement for Elasticsearch without licensing concerns. The built-in security plugin, ISM for index lifecycle management, and alerting capabilities make it suitable for log analytics, application search, and observability use cases. For production, deploy a minimum 3-node cluster with dedicated master nodes, configure ISM policies to manage index retention, and use the security plugin with TLS from day one.