Setting Up OpenSearch as an Elasticsearch Alternative
OpenSearch is a fork of Elasticsearch 7.10, created by Amazon after Elastic's license change in 2021. Technically almost identical to ES 7.x, licensed under Apache 2.0, actively developed by AWS and the community. If you can't use proprietary Elastic Stack (X-Pack) or are locked into AWS, OpenSearch is a practical alternative. Key differences from ES 8.x: missing some ES 8.x features, but has its own ML capabilities through OpenSearch ML Commons.
Comparison with Elasticsearch
OpenSearch maintained compatibility with Elasticsearch 7.x API — most ES 7.x clients work without changes. ES 8.x added breaking changes (mandatory TLS, security API changes, type renames), so direct compatibility is broken.
What OpenSearch has that free Elasticsearch lacks:
- Security (TLS, authentication, RBAC) without paid license
- Alerting, anomaly detection in basic version
- Index Management (index lifecycle) without Elastic paid tier
- SQL support
What only Elasticsearch has:
- EQL (Event Query Language)
- Esql
- Some Elastic ML features
OpenSearch Installation
Docker Compose for production-like cluster:
version: '3'
services:
opensearch-node1:
image: opensearchproject/opensearch:2.13.0
container_name: opensearch-node1
environment:
- cluster.name=opensearch-cluster
- node.name=opensearch-node1
- discovery.seed_hosts=opensearch-node1,opensearch-node2
- cluster.initial_cluster_manager_nodes=opensearch-node1,opensearch-node2
- bootstrap.memory_lock=true
- "OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g"
- DISABLE_INSTALL_DEMO_CONFIG=true
- DISABLE_SECURITY_PLUGIN=false
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
volumes:
- opensearch-data1:/usr/share/opensearch/data
- ./opensearch.yml:/usr/share/opensearch/config/opensearch.yml
- ./certs/root-ca.pem:/usr/share/opensearch/config/root-ca.pem
- ./certs/node1.pem:/usr/share/opensearch/config/node1.pem
- ./certs/node1-key.pem:/usr/share/opensearch/config/node1-key.pem
ports:
- 9200:9200
- 9600:9600
opensearch-node2:
image: opensearchproject/opensearch:2.13.0
container_name: opensearch-node2
environment:
- cluster.name=opensearch-cluster
- node.name=opensearch-node2
- discovery.seed_hosts=opensearch-node1,opensearch-node2
- cluster.initial_cluster_manager_nodes=opensearch-node1,opensearch-node2
- bootstrap.memory_lock=true
- "OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g"
- DISABLE_INSTALL_DEMO_CONFIG=true
- DISABLE_SECURITY_PLUGIN=false
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- opensearch-data2:/usr/share/opensearch/data
- ./opensearch.yml:/usr/share/opensearch/config/opensearch.yml
- ./certs/root-ca.pem:/usr/share/opensearch/config/root-ca.pem
- ./certs/node2.pem:/usr/share/opensearch/config/node2.pem
- ./certs/node2-key.pem:/usr/share/opensearch/config/node2-key.pem
opensearch-dashboards:
image: opensearchproject/opensearch-dashboards:2.13.0
container_name: opensearch-dashboards
ports:
- 5601:5601
environment:
OPENSEARCH_HOSTS: '["https://opensearch-node1:9200","https://opensearch-node2:9200"]'
DISABLE_SECURITY_DASHBOARDS_PLUGIN: "false"
volumes:
opensearch-data1:
opensearch-data2:
opensearch.yml:
cluster.name: opensearch-cluster
network.host: 0.0.0.0
plugins.security.ssl.transport.pemcert_filepath: node1.pem
plugins.security.ssl.transport.pemkey_filepath: node1-key.pem
plugins.security.ssl.transport.pemtrustedcas_filepath: root-ca.pem
plugins.security.ssl.transport.enforce_hostname_verification: false
plugins.security.ssl.http.enabled: true
plugins.security.ssl.http.pemcert_filepath: node1.pem
plugins.security.ssl.http.pemkey_filepath: node1-key.pem
plugins.security.ssl.http.pemtrustedcas_filepath: root-ca.pem
plugins.security.allow_unsafe_democertificates: false
plugins.security.allow_default_init_securityindex: true
plugins.security.authcz.admin_dn:
- 'CN=admin,OU=client,O=client,L=test,C=de'
plugins.security.nodes_dn:
- 'CN=node1.example.com,OU=test,O=test,L=test,C=de'
- 'CN=node2.example.com,OU=test,O=test,L=test,C=de'
plugins.security.audit.type: internal_opensearch
plugins.security.enable_snapshot_restore_privilege: true
plugins.security.check_snapshot_restore_write_privileges: true
plugins.security.restapi.roles_enabled: ["all_access", "security_rest_api_access"]
TLS Certificate Generation
OpenSearch provides utility for generating demo certificates (test only):
# For production — own CA
openssl genrsa -out root-ca-key.pem 2048
openssl req -new -x509 -sha256 -key root-ca-key.pem -out root-ca.pem \
-days 3650 -subj "/C=UA/O=MyOrg/CN=root-ca"
# Node certificate
openssl genrsa -out node1-key.pem 2048
openssl req -new -key node1-key.pem -out node1.csr \
-subj "/C=UA/O=MyOrg/CN=node1.example.com/OU=test"
openssl x509 -req -in node1.csr -CA root-ca.pem -CAkey root-ca-key.pem \
-CAcreateserial -out node1.pem -days 3650 -sha256
Security Plugin Configuration
After first startup initialize security index:
# Inside container
docker exec -it opensearch-node1 bash
/usr/share/opensearch/plugins/opensearch-security/tools/securityadmin.sh \
-cd /usr/share/opensearch/config/opensearch-security/ \
-icl -nhnv \
-cacert /usr/share/opensearch/config/root-ca.pem \
-cert /usr/share/opensearch/config/admin.pem \
-key /usr/share/opensearch/config/admin-key.pem
# Change admin password
curl -XPUT "https://localhost:9200/_plugins/_security/api/internalusers/admin" \
-H 'Content-Type: application/json' \
-u 'admin:admin' \
--cacert root-ca.pem \
-d '{"password": "NewSecurePassword123!", "backend_roles": ["admin"]}'
ISM (Index State Management)
OpenSearch replacement for Elastic ILM:
PUT _plugins/_ism/policies/logs-policy
{
"policy": {
"description": "Logs lifecycle policy",
"default_state": "hot",
"states": [
{
"name": "hot",
"actions": [
{
"rollover": {
"min_index_age": "7d",
"min_size": "50gb"
}
}
],
"transitions": [
{
"state_name": "warm",
"conditions": { "min_index_age": "7d" }
}
]
},
{
"name": "warm",
"actions": [
{ "replica_count": { "number_of_replicas": 0 } },
{ "force_merge": { "max_num_segments": 1 } }
],
"transitions": [
{
"state_name": "delete",
"conditions": { "min_index_age": "60d" }
}
]
},
{
"name": "delete",
"actions": [{ "delete": {} }],
"transitions": []
}
],
"ism_template": [{
"index_patterns": ["logs-*"],
"priority": 100
}]
}
}
Client Connection
OpenSearch provides its own clients compatible with ES 7.x API:
PHP:
composer require opensearch-project/opensearch-php
use OpenSearch\ClientBuilder;
$client = ClientBuilder::create()
->setHosts(['https://opensearch-node1:9200'])
->setBasicAuthentication('admin', 'NewSecurePassword123!')
->setSSLVerification('/path/to/root-ca.pem')
->build();
Python:
pip install opensearch-py
from opensearchpy import OpenSearch
client = OpenSearch(
hosts=['https://opensearch-node1:9200'],
http_auth=('admin', 'NewSecurePassword123!'),
use_ssl=True,
verify_certs=True,
ca_certs='/path/to/root-ca.pem',
)
If using elasticsearch-php 7.x — it works with OpenSearch without changes (API compatible). For ES 8.x client you need opensearch-php.
Migration from Elasticsearch
Snapshot restore works between ES 7.x and OpenSearch:
# On ES: create snapshot
PUT /_snapshot/backup_repo
{
"type": "fs",
"settings": { "location": "/mnt/backup" }
}
POST /_snapshot/backup_repo/snapshot_1
{ "indices": "products,orders" }
# Mount same volume on OpenSearch node
# In OpenSearch: register repository and restore
PUT /_snapshot/backup_repo
{
"type": "fs",
"settings": { "location": "/mnt/backup" }
}
POST /_snapshot/backup_repo/snapshot_1/_restore
{ "indices": "products,orders" }
Direct migration ES 8.x → OpenSearch impossible due to format incompatibility. Need reindex via _reindex API or export/import via logstash.
Timeline
Deploying OpenSearch cluster from 3 nodes with TLS and Security plugin — 2–3 working days. Migration from Elasticsearch 7.x — 1 additional day. Setting up ISM policies and OpenSearch Dashboards — 1 more day.







