Introduction
What is Elasticsearch?
Elasticsearch is an open-source, distributed search and analytics engine built on top of Apache Lucene. It is designed for full-text search, structured queries, and near real-time indexing of large volumes of data. Elasticsearch is widely used for powering application search, log analytics, and data visualization.
Why use Elasticsearch with a website?
Traditional relational databases like MySQL are great for structured data, but they are not optimized for complex search queries across large amounts of text. Elasticsearch provides:
- Full-text search with relevance scoring and highlighting
- Fast performance for complex queries and aggregations
- Scalability from a single node to clusters of many nodes
- Analytics across logs, metrics, or content
For example, if you integrate Elasticsearch with WordPress, you can:
- Provide advanced search capabilities for your visitors (autocomplete, fuzzy matches, filters)
- Offload heavy search queries from MySQL, improving overall site performance
- Build custom search-driven features such as faceted search, related posts, or product filters in WooCommerce
1. Prerequisites (on the host)
Before we launch containers, we make the host predictable and friendly to Elasticsearch. The Docker extension in Plesk provides a convenient UI, but Elasticsearch still depends on a couple of Linux kernel and filesystem realities: vm.max_map_count must be high enough for Lucene’s memory‑mapped segments, and your data path should be persistent and writable by the Elasticsearch user (UID 1000). Doing this first prevents confusing bootstrap errors later and makes the setup repeatable across servers.
- Install Docker & Docker Extension
- In Plesk → Extensions → install Docker if not already present.
- Raise
vm.max_map_count(required by Elasticsearch):
1234echo "vm.max_map_count=262144" | sudo tee /etc/sysctl.d/99-elasticsearch.confsudo sysctl --systemsysctl vm.max_map_count # should show 262144 - Create a persistent data directory
123sudo mkdir -p /var/lib/elasticsearch-datasudo chown 1000:0 /var/lib/elasticsearch-data
2. Deploy via Plesk GUI (Docker extension)
Plesk’s Docker extension wraps the common docker run options in a clean form: image/tag selection, environment variables, volumes, and ports. We’ll use the official Docker image (elasticsearch:9.1.3) and configure a single‑node instance via discovery.type=single-node. Security is enabled by default in modern Elasticsearch; we keep credentials in env vars and let Plesk handle the proxying so the container itself stays off the public internet.
- Go to Plesk → Docker → + Add Container
- Image: Search for
elasticsearchin the box. Select the one tagged Official from Docker Hub (https://hub.docker.com/_/elasticsearch/). Use version9.1.3. - Environment variables:
discovery.type = single-node- Choose ONE security setup:
- Recommended (security ON, default in 8+ and 9+):
ELASTIC_PASSWORD = <your-strong-password>
- Dev only (security OFF, not for internet exposure):
xpack.security.enabled = false
- Recommended (security ON, default in 8+ and 9+):
- Heap sizing (adjust to available RAM):
ES_JAVA_OPTS = -Xms1g -Xmx1g
- If you are using Plesk Proxy Rules (HTTP upstream)
- Add:
xpack.security.http.ssl.enabled = false - Rationale: Plesk’s proxy forwards HTTP to the container. With security enabled, Elasticsearch’s HTTP layer defaults to HTTPS. Without this setting you’ll see errors like
received plaintext http traffic on an https channeland a 502 Bad Gateway from Nginx. Turning off HTTP SSL in ES lets Plesk terminate TLS at the domain while talking plain HTTP to the container.
- Add:
- Volumes:
- Host:
/var/lib/elasticsearch-data - Container:
/usr/share/elasticsearch/data
- Host:
- Ports:
- Recommended: Do not publish directly. Use Proxy Rules (next step).
- If needed: Publish
9200 → 9200. For single-node, 9300 is not required.
- Ulimits note:
- The Plesk GUI does not allow adding arbitrary
--ulimitflags. In most single-node cases, Elasticsearch runs fine without them. - If you encounter errors related to
memlockornofilelimits, see the CLI option below to apply these parameters manually.
- The Plesk GUI does not allow adding arbitrary
3. Proxy via Domain (recommended)
Elasticsearch’s REST API is powerful—and that’s exactly why it shouldn’t be exposed directly. A path‑based proxy keeps 9200 private, terminates TLS at your domain, and lets you place simple access controls in front if needed. In this guide, Plesk forwards HTTP upstream to the container, so we tell Elasticsearch to speak plain HTTP on its internal port and let Plesk own the TLS boundary.
Use Plesk’s proxy so the container stays private and TLS terminates at the domain:
- Path rule: Plesk → Domains → your domain → Docker Proxy Rules → Add Rule
- Location:
/es - Forward to: Elasticsearch container, port 9200 (HTTP upstream)
- Location:
- Important: Because the upstream is HTTP, ensure the container has
xpack.security.http.ssl.enabled = falsein its Environment variables, then restart. - When to skip proxy rules: If you want ES to keep HTTPS on port 9200 internally, don’t use proxy rules; instead publish 9200 and access it directly (bind to 127.0.0.1 or firewall it).
Troubleshooting: If you see 502 Bad Gateway and logs like received plaintext http traffic on an https channel, the ES container is expecting HTTPS. Set xpack.security.http.ssl.enabled=false and restart.
4. Testing the Installation
Short smoke tests confirm connectivity, auth, and the proxy path. A healthy Elasticsearch responds with a small JSON document (including its cluster_name and version). For Kibana, the browser UI should load under the /kibana subpath without broken assets when basePath settings are correct. If you see TLS or 502 errors, the culprit is almost always a mismatch between HTTPS at the proxy and HTTP inside the container.
- Elasticsearch (security ON, proxied path):
12curl -u elastic:<ELASTIC_PASSWORD> https://yourdomain/es -k-konly if your domain cert chain isn’t trusted on the host. If you published 9200 locally, you can also test directly:curl -u elastic:<ELASTIC_PASSWORD> http://127.0.0.1:9200. - Elasticsearch (security OFF, dev only):
12curl http://yourdomain/es - Kibana (after configuring basePath—see Section 7):
Visithttps://yourdomain/kibanain a browser. You should see the Kibana UI. Log in using an Elasticsearch user (e.g.,elastic).
Check container logs in Plesk → Docker → Container → Logs if you hit issues.
5. Command Line Option (with ulimit)
Sometimes you need flags the Plesk GUI doesn’t expose (like --ulimit) or you want an exact, scriptable command you can run in CI. The CLI example mirrors the GUI configuration but adds resource flags where appropriate. You can mix approaches—run Elasticsearch under Plesk for easy proxy rules, but keep a CLI recipe handy for repeatable builds on other hosts.
If you prefer to run the container outside of Plesk, or if you need to add --ulimit flags:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
sudo mkdir -p /var/lib/elasticsearch-data sudo chown 1000:0 /var/lib/elasticsearch-data docker run -d --name es01 --restart unless-stopped \ -p 127.0.0.1:9200:9200 \ -e discovery.type=single-node \ -e ELASTIC_PASSWORD='ChangeMe' \ -e ES_JAVA_OPTS="-Xms1g -Xmx1g" \ --ulimit memlock=-1:-1 \ --ulimit nofile=65536:65536 \ -v /var/lib/elasticsearch-data:/usr/share/elasticsearch/data \ elasticsearch:9.1.3 # Test curl -u elastic:ChangeMe https://127.0.0.1:9200 -k |
6. Common Pitfalls & Troubleshooting
When something doesn’t start, check in this order: (1) host prerequisites (sysctl, disk perms), (2) container logs in Plesk, (3) environment variables (typos, booleans), and (4) networking (proxy rules vs. direct port publishing, path prefixes). Most problems show up as a single, telling message early in the container log—fix that root cause and the rest usually falls into place.
vm.max_map_count [65530] is too low→ You missed the prerequisite step. Apply and restart.- Permission errors on data dir → Ensure
/var/lib/elasticsearch-dataexists and is owned by UID 1000. 502 Bad Gatewayfrom Plesk /received plaintext http traffic on an https channelin ES logs → Addxpack.security.http.ssl.enabled=falseto ES env when using Plesk Proxy Rules (HTTP upstream) and restart.- Kibana rejects
elasticuser → Newer Kibana forbids using theelasticsuperuser for internal writes. Use a service account token instead (Section 7). - ES API calls via
/espath fail → Plesk forwards the/esprefix to ES; ES APIs expect paths at root (e.g.,/_security/...). Call ES directly on its container IP or localhost, or configure the proxy to strip the prefix. FileAlreadyExistsException: elasticsearch.keystore.tmpat startup → Harmless if it appears once and ES continues to start. It’s usually a leftover temp keystore file from a prior boot. If it repeats on every start:- Inspect & remove the tmp file inside the container:
123docker exec -it <ES_CONTAINER_NAME> sh -lc 'ls -l /usr/share/elasticsearch/config/elasticsearch.keystore* || true'docker exec -it <ES_CONTAINER_NAME> sh -lc 'rm -f /usr/share/elasticsearch/config/elasticsearch.keystore.tmp' - Ensure ownership of the config dir (UID 1000 is the elasticsearch user):
12docker exec -it <ES_CONTAINER_NAME> sh -lc 'chown -R 1000:0 /usr/share/elasticsearch/config' - Verify keystore:
12docker exec -it <ES_CONTAINER_NAME> /usr/share/elasticsearch/bin/elasticsearch-keystore list - If you’re bind-mounting
/usr/share/elasticsearch/config, ensure host perms are1000:0and rebuild the container so the tmp file isn’t persisted.
- Inspect & remove the tmp file inside the container:
- Self-signed/TLS warnings → Use
-kfor quick curl tests, or put Nginx/Apache in front with proper TLS. In this guide, TLS terminates at Plesk; ES/Kibana speak HTTP behind the proxy. - High CPU during first boot → Initial migrations/package installs can spike CPU; it should settle after a few minutes.
7. Optional: Add Kibana (UI)
Kibana is the UI lens on top of Elasticsearch: dashboards, visualizations, Dev Tools, and management apps. In recent versions Kibana must authenticate to Elasticsearch with a service account token (not the elastic superuser), and when you run it behind a reverse proxy you’ll also set a basePath so assets load under a sub‑URL like /kibana. The steps below create the token, wire up the env vars, and route Kibana cleanly through Plesk.
Kibana is the official web UI for Elasticsearch. It provides dashboards, visualizations, Dev Tools for queries, and app modules (APM, Security, Fleet, etc.).
7.1 Create a Kibana service account token
Kibana must not use the elastic superuser. Create a service account token that Kibana will use to talk to Elasticsearch.
A) API via ES container IP or localhost (no proxy prefix):
|
1 2 3 4 5 6 7 8 9 10 |
# If you published 9200 on localhost: TOKEN_NAME=kibana-token-$(date +%F) curl -u elastic:'<ELASTIC_PASSWORD>' \ -X POST "http://127.0.0.1:9200/_security/service/elastic/kibana/credential/token/$TOKEN_NAME" # Or using the ES container IP (replace with your 172.x IP): ES_IP=172.17.0.2 curl -u elastic:'<ELASTIC_PASSWORD>' \ -X POST "http://$ES_IP:9200/_security/service/elastic/kibana/credential/token/$TOKEN_NAME" |
Copy the token.value from the JSON response.
B) CLI inside the ES container:
|
1 2 3 4 |
docker exec -it <ES_CONTAINER_NAME> \ /usr/share/elasticsearch/bin/elasticsearch-service-tokens \ create elastic/kibana "kibana-token-$(date +%F)" |
This prints the token.
Note: Don’t call the API through your Plesk
/espath; ES will see/es/_security/...and return “no handler found.”
7.2 Deploy Kibana in Plesk
- Plesk → Docker → + Add Container
- Image:
kibana:9.1.3(official Docker image) - Environment variables:
- Core connectivity
ELASTICSEARCH_HOSTS = http://<ES_CONTAINER_IP>:9200
(or a Docker link/alias to the ES container; do not use your proxied/esURL)ELASTICSEARCH_SERVICEACCOUNTTOKEN = <token.value from step 7.1>
- Proxy subpath (so assets load under /kibana)
SERVER_BASEPATH = /kibanaSERVER_REWRITEBASEPATH = trueSERVER_PUBLICBASEURL = https://<yourdomain>/kibana
- Stabilize sessions & features (removes key warnings)
XPACK_SECURITY_ENCRYPTIONKEY = <32+ char random>XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY = <32+ char random>XPACK_REPORTING_ENCRYPTIONKEY = <32+ char random>
Generate with:openssl rand -base64 32
- Optional: silence Fleet errors for now
XPACK_FLEET_ENABLED = false
- Core connectivity
- Ports / Proxy Rules:
- Don’t publish 5601 publicly. Instead, create a Plesk Docker Proxy Rule on your domain:
Location:/kibana→ Container port:5601
- Don’t publish 5601 publicly. Instead, create a Plesk Docker Proxy Rule on your domain:
- Restart the Kibana container.
- Test: Visit
https://<yourdomain>/kibanaand sign in. You should see Kibana running and connected to ES.
7.3 Notes & optional features
- If you later want Fleet/Integrations, remove
XPACK_FLEET_ENABLED=false, restart, then use Management → Fleet to complete setup. If installation errors mention missing component templates, rerun after the first boot completes. - Warnings like “Session cookies will be transmitted over insecure connections” are expected when Kibana speaks HTTP to Plesk. To remove them, you’d need HTTPS between Plesk and Kibana (uncommon with Docker Proxy Rules).
8. Notes
These notes capture a couple of defaults and choices we’re making (image versions, proxy model). If you upgrade versions later, keep the same patterns—host prerequisites first, private containers, TLS at the proxy, and small, explicit env settings.
- Version used here: Elasticsearch 9.1.3 (official image).
- Plesk’s Docker extension handles networking and proxying, but you can also manage the container via CLI if you need extra parameters like
--ulimit.
9. Sizing & Resource Limits (Memory/CPU)
Elasticsearch performance is a balancing act: heap for the JVM, plus plenty of off‑heap memory for Lucene and the filesystem cache. Start with conservative limits so the host remains healthy alongside MySQL, PHP‑FPM, and Nginx, then iterate with real traffic. Kibana’s footprint is comparatively small—size it for features you actually use and grow only if you see OOMs or GC pressure in the logs.
Below are reasonable starting points for a single-node Elasticsearch + Kibana powering WordPress search. Adjust up/down based on traffic, index size, and features (e.g., Fleet, ML, Security Solution). Always leave headroom on the host for MySQL, Nginx/Apache, PHP-FPM, and the OS.
9.1 Quick sizing tiers
| Tier (free RAM on host) | Elasticsearch container memory limit | ES_JAVA_OPTS heap (-Xms = -Xmx) |
Kibana container memory limit | NODE_OPTIONS (Kibana) |
|---|---|---|---|---|
| Tiny (≈2 GB free) | 1 GB | 512m | 384–512 MB | --max-old-space-size=256 |
| Small (≈4 GB free) | 2 GB | 1g | 512–768 MB | --max-old-space-size=512 |
| Medium (≈8 GB free) | 4 GB | 2g | 1–1.5 GB | --max-old-space-size=1024 |
| Large (≥16 GB free) | 8 GB | 4g | 1.5–2 GB | --max-old-space-size=1536 |
Rules of thumb:
- Set ES heap to ~50% of the ES container memory limit (don’t exceed what the host can spare). The other ~50% is used by Lucene off‑heap and filesystem cache.
- Keep
-Xmsand-Xmxequal. - Start Kibana small; it’s mostly UI. Increase if you enable heavy apps (e.g., Security Solution, Reporting).
9.2 Where to set limits
Plesk GUI:
- Plesk → Docker → (container) → Edit → set Memory limit (and optional CPU limit).
- Set env vars:
- Elasticsearch:
ES_JAVA_OPTS=-Xms1g -Xmx1g(use the heap that matches your tier) - Kibana:
NODE_OPTIONS=--max-old-space-size=512(value in MB)
- Elasticsearch:
Docker CLI (examples):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# Elasticsearch (2 GB limit, 1 GB heap) docker run -d --name es01 --restart unless-stopped \ --memory=2g --memory-reservation=1.5g --memory-swap=2g \ -e ES_JAVA_OPTS="-Xms1g -Xmx1g" -e discovery.type=single-node \ -v /var/lib/elasticsearch-data:/usr/share/elasticsearch/data \ -p 127.0.0.1:9200:9200 elasticsearch:9.1.3 # Kibana (512 MB limit) docker run -d --name kib01 --restart unless-stopped \ --memory=512m --memory-reservation=384m --memory-swap=512m \ -e ELASTICSEARCH_HOSTS="http://172.17.0.2:9200" \ -e ELASTICSEARCH_SERVICEACCOUNTTOKEN="<token>" \ -e SERVER_BASEPATH="/kibana" -e SERVER_REWRITEBASEPATH="true" \ -e SERVER_PUBLICBASEURL="https://yourdomain/kibana" \ -e NODE_OPTIONS="--max-old-space-size=512" \ -p 127.0.0.1:5601:5601 kibana:9.1.3 |
Swap note: Set --memory-swap equal to --memory to effectively disable swap for the container (often preferable for ES). If you allow swap, keep it small to avoid latency spikes.
9.3 Monitoring & tuning
- Watch usage:
docker statsand ES API/_nodes/stats/jvm,process?pretty - If ES OOMs: raise the container memory and heap proportionally, or reduce query/indexing concurrency.
- If Kibana OOMs: bump container memory and
--max-old-space-size(e.g., 768–1024). Consider disabling heavy plugins you don’t use (e.g., setXPACK_FLEET_ENABLED=false). - GC tuning: The defaults are fine for small deployments; focus on right‑sizing heap first.
10. Next Steps
With Elasticsearch and Kibana stable behind Plesk, connect your website. For WordPress, tools like ElasticPress can offload search queries and enable relevance tuning. Index your content during a low‑traffic window, watch docker stats as caches warm up, and revisit sizing after a day of real use. When everything is quiet, capture screenshots and publish the polished blog post.
- Verify with
docker statsthat ES sits well below its limit under load; aim for 60–70% utilization. - Revisit sizing after enabling WordPress plugins (e.g., ElasticPress) and after initial indexing.




