Skip to content

OpenSearch Setup

RequirementVersion
Java (OpenJDK)21 (required for OpenSearch 3.x)
Node.js20+
RAM8GB minimum (6GB available)
Disk70GB free

The opensearch-mgr CLI provides unified management for all OpenSearch services.

Terminal window
# Installation (already done on this server)
# The CLI is symlinked globally from /root/opensearch/cli/opensearch-mgr.js
# Verify
opensearch-mgr --version
CommandDescription
start [service]Start opensearch, dashboards, or all
stop [service]Stop services
restart [service]Restart services
statusShow status of all services
healthHealth check endpoints
build [service]Build from source
deployDeploy built OpenSearch to /opt
logs [service]View logs (-f to follow)
watchWatchdog mode with auto-restart
install-serviceInstall systemd services
uiInteractive menu mode
ingest <path>Bulk ingest log files
watch-logs <path>Continuous log file monitoring
ingest-statusShow ingestion statistics
CommandDescription
semantic-setupSetup ML model and semantic indexes
semantic-reindex [index]Reindex data for semantic search
semantic <query>Perform semantic search
semantic-statusCheck ML model and index status
model-ensureEnsure ML model is loaded
Terminal window
opensearch-mgr status # Human-readable
opensearch-mgr status --json # JSON for automation

/opt/opensearch/config/opensearch.yml:

cluster.name: opensearch-dev
node.name: node-1
network.host: 0.0.0.0
discovery.type: single-node

OpenSearch is exposed publicly at https://analyticsapi.logrite.ai (Cloudflare → :9200 direct). Because the browser-side client (OpenSearchContext.jsx) sends Authorization headers, http.cors.allow-credentials must be true — which means http.cors.allow-origin cannot be "*". When that combination is set, OpenSearch silently drops all Access-Control-Allow-* headers and every browser request fails CORS.

Use a regex allowlist instead:

http.cors.enabled: true
http.cors.allow-origin: "/https?:\\/\\/(app|logriteiq|staging)\\.logrite\\.ai|https?:\\/\\/localhost(:[0-9]+)?/"
http.cors.allow-methods: OPTIONS, HEAD, GET, POST, PUT, DELETE
http.cors.allow-headers: X-Requested-With, Content-Type, Content-Length, Authorization
http.cors.allow-credentials: true

Restart after editing: opensearch-mgr restart opensearch.

Verify:

Terminal window
curl -I -X OPTIONS https://analyticsapi.logrite.ai/_cluster/health \
-H "Origin: https://app.logrite.ai" \
-H "Access-Control-Request-Method: GET"
# Expect: access-control-allow-origin: https://app.logrite.ai

To add a new browser origin, extend the regex — do not switch back to "*".

/root/opensearch/OpenSearch-Dashboards/config/opensearch_dashboards.yml:

server.host: "127.0.0.1"
server.basePath: "/pxe"
server.rewriteBasePath: true
opensearch.hosts: ["http://localhost:9200"]
opensearch.ssl.verificationMode: none
server.cors:
origin:
- "https://app.logrite.ai"
- "https://logrite.deployd.io"
- "https://analytics.logrite.ai"
additionalHeaders:
- osd-xsrf
- osd-version
- kbn-version
- Content-Type
- Authorization
credentials: true

Dashboards is bound to 127.0.0.1:5601 and fronted by Apache at analytics.logrite.ai — never expose it directly. credentials: true is required because the frontend calls /pxe/internal/search/* with cookies/Authorization; with credentials: false the browser rejects the response. allow-origin must be an explicit list (not *) when credentials is true.

Dashboards is reached externally via https://analytics.logrite.ai/pxe/* → Cloudflare → Apache (:80) → 127.0.0.1:5601. The Apache vhost lives at /etc/apache2/sites-available/analytics.logrite.ai.conf (versioned in LogriteMasterControl at config/apache/analytics.logrite.ai.conf):

<VirtualHost *:80>
ServerName analytics.logrite.ai
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://127.0.0.1:5601/
ProxyPassReverse / http://127.0.0.1:5601/
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) ws://127.0.0.1:5601/$1 [P,L]
RequestHeader set X-Forwarded-Proto "https"
ProxyTimeout 300
ErrorLog ${APACHE_LOG_DIR}/analytics-error.log
CustomLog ${APACHE_LOG_DIR}/analytics-access.log combined
</VirtualHost>

Enable with a2ensite analytics.logrite.ai && apache2ctl configtest && systemctl reload apache2. Cloudflare’s Tunnel / origin for analytics.logrite.ai must route to this host on :80 — check the Cloudflare Zero Trust dashboard if the edge returns 502 while curl -H 'Host: analytics.logrite.ai' http://127.0.0.1/pxe/api/status returns 200 locally.

Terminal window
# Start everything
opensearch-mgr start all
# Start only OpenSearch (wait for green health)
opensearch-mgr start opensearch
# Start only Dashboards
opensearch-mgr start dashboards
Terminal window
# Rebuild OpenSearch engine
opensearch-mgr build opensearch
# Deploy to /opt/opensearch
opensearch-mgr deploy
# Rebuild Dashboards (takes 5-15 minutes)
opensearch-mgr build dashboards
Terminal window
# Pull latest from official opensearch-project
git fetch upstream
git checkout main
git merge upstream/main
git push origin main

For bootstrapping OpenSearch on a fresh server, an export bundle of live state lives at /root/opensearch/exports/:

FileContents
kibana/saved-objects.ndjsonDashboards index-patterns, visualizations, dashboards
detectors/detectors.jsonAnomaly detectors (production set)
monitors/monitors.jsonAlert monitors
destinations/destinations.jsonNotification channels (Discord webhooks)
import.shRe-imports all four into a target cluster
logrite-opensearch-bundle.tgzSingle tarball with all of the above + sample NDJSON + dashboard creator scripts

Restore on the new server (after opensearch-mgr start all is healthy and source data is ingested):

Terminal window
scp logrite-opensearch-bundle.tgz newserver:/root/
ssh newserver "cd /root/opensearch && tar xzf /root/logrite-opensearch-bundle.tgz && bash exports/import.sh"
Terminal window
# Install systemd services
opensearch-mgr install-service
# Enable and start
sudo systemctl enable opensearch opensearch-dashboards
sudo systemctl start opensearch
sudo systemctl start opensearch-dashboards

Logrite maintains 4 composable index templates for its own indices. These are managed by opensearch/scripts/create-warden-indexes.js in the LogriteOpenSearch repo.

TemplatePatternRetentionPurpose
log-analytics-templatelog-analytics-*90 daysCollector agent log documents
agent-flagsagent-flags-*90 daysCompliance & anomaly flag findings (Phase 3)
warden-requestswarden-requests-*90 daysAI Warden request captures
relay-compliance-eventsrelay-compliance-events-*180 daysRelay compliance violation events

Each template has a matching ISM policy (warden-requests-retention, relay-compliance-events-retention, agent-flags-retention, collector-logs-retention) attached via ism_template. Policies auto-attach to new indexes matching the pattern — no manual steps needed when a new workspace index is created.

Terminal window
# Apply / update all 4 templates and ISM policies (idempotent — safe to re-run)
OPENSEARCH_USERNAME=<user> OPENSEARCH_PASSWORD=<pass> \
node opensearch/scripts/create-warden-indexes.js

The script is idempotent — all operations are PUT so re-running it is safe.

start-all runs this automatically as a pre-flight after OpenSearch is healthy (via the ensureIndexTemplates() module export). Manual runs are only needed to apply template changes outside of a full restart.

The dashboards-svc user has permission to write index templates (PUT /_index_template/*) but not ISM policies (cluster:admin/opendistro/ism/policy/write). ISM policy writes require the admin user. Since policies rarely change, this is normally not an issue — templates are what need re-applying after field mapping changes.

  1. Root user — OpenSearch refuses to start as root. The opensearch system user is used.
  2. First Dashboards start — Takes several minutes for bundle compilation.
  3. Security plugin — Not included in source builds (the legacy opensearch-mgr install on /opt/opensearch/). The official Docker image used in Coolify (opensearchproject/opensearch:3.3.1) ships with it pre-installed. See Security Plugin for users/roles management via the Dashboards UI, and Security Hardening for the platform-wide checklist.
  4. PID management — PIDs tracked in /var/run/opensearch/. Always use opensearch-mgr for lifecycle management.