How to Install Elasticsearch 8 on Ubuntu 24.04 VPS: Search and Analytics Engine
Elasticsearch is the most widely deployed distributed search and analytics engine in the world, powering everything from product search on e-commerce sites to log aggregation at petabyte scale. This guide walks you through a production-grade install on Ubuntu 24.04 -- from the first apt update through TLS auto-configuration, Kibana enrollment, JVM heap tuning, S3/MinIO snapshot backups, and index lifecycle management (ILM).
By the end of the tutorial you will have a single-node Elasticsearch 8 cluster with HTTPS and password authentication, a Kibana UI accessible over TLS, automated nightly snapshots, and ILM policies that roll over old indices to save disk.
Table of Contents
What is Elasticsearch?
Elasticsearch is an open distributed search and analytics engine built on Apache Lucene. It stores JSON documents in shards, distributes them across nodes, and exposes a REST API for near-real-time search, aggregation, and vector similarity queries. Together with Kibana (the UI), Beats (lightweight data shippers), and Logstash (an ETL pipeline), it forms what has been known for a decade as the "ELK stack" -- now simply the Elastic Stack.
Typical workloads include:
- Application search -- full-text product search, typeahead, faceted navigation on stores and catalogs
- Log aggregation and observability -- centralized log ingestion from servers, containers and Kubernetes clusters, with Kibana dashboards for SREs
- Security analytics (SIEM) -- threat detection, audit log retention, correlation rules
- Vector search / semantic retrieval -- dense vector fields for retrieval-augmented generation (RAG) pipelines using embeddings from models like
nomic-embed-textor OpenAItext-embedding-3 - Business analytics -- high-cardinality aggregations over billions of events that are impractical in OLTP databases
- Geospatial search --
geo_pointandgeo_shapequeries for maps, delivery, logistics
dense_vector with HNSW) is generally available, and x-pack features previously reserved for paid tiers are included in the free Basic license.Why Self-Host Elasticsearch vs. Elastic Cloud?
Elastic offers a managed SaaS product called Elastic Cloud on AWS, GCP and Azure. It is a fine product, but self-hosting on your own VPS has concrete advantages:
- Predictable flat-rate cost -- Elastic Cloud bills on hourly node hours and data transfer. A 2-node cluster with 8 GB RAM each on Elastic Cloud runs roughly EUR 180-250/month. The same workload on a single CloudCore Professional VPS costs EUR 19.99/month flat.
- Data sovereignty -- Your search indices, logs and user queries stay on infrastructure you control. Important for GDPR, HIPAA, and any contract with a "no third-party SaaS" clause.
- No surprise egress bills -- Elastic Cloud charges for data transferred out. A VPS includes a generous bandwidth allocation with predictable overage pricing.
- Full kernel and OS control -- Tune
vm.swappiness,vm.max_map_count, filesystem choice, I/O scheduler. You cannot do this on a managed cluster. - Any plugin, any version -- Install community analyzers (ICU, Smart Chinese, Kuromoji), custom snapshot plugins, or run an older version while you prepare a migration. Managed offerings restrict this.
- Integrate with local services -- Run Elasticsearch alongside your app, Redis, and PostgreSQL on the same private network. Sub-millisecond latency is impossible over the public internet from a managed cluster.
Licensing: SSPL and the Elastic License v2
A brief but important note. Starting with version 7.11, Elasticsearch is no longer Apache 2.0 licensed. The current distribution is dual-licensed under:
- Server Side Public License (SSPL) v1
- Elastic License v2 (ELv2)
- You can freely self-host Elasticsearch on your own servers -- for internal use, customer-facing products, SaaS apps, anything. The Basic tier is free and includes TLS, RBAC, snapshot lifecycle management, and the
dense_vectorfield type. - You cannot offer Elasticsearch itself as a managed/hosted service to third parties. This is the clause that spawned AWS's OpenSearch fork in 2021.
- You cannot remove or circumvent the license check inside the binaries.
Full license text: elastic.co/licensing/elastic-license.
Prerequisites
Before you begin, make sure you have:
- A VPS running Ubuntu 24.04 LTS with root or sudo access
- At least 4 vCPU and 8 GB RAM (16 GB recommended for production)
- At least 50 GB of SSD/NVMe storage (indices grow quickly)
- A fully qualified hostname or public IP for accessing Kibana externally
- SSH access to the server
Recommended Plan: CloudCore Professional>
For a production single-node Elasticsearch + Kibana setup, we recommend the CloudCore Professional plan:>
- 6 vCPU cores
- 12 GB RAM (enough for 4 GB JVM heap + Lucene cache + Kibana)
- 100 GB NVMe SSD
- Unmetered bandwidth
- EUR 19.99/month>
For log aggregation workloads receiving more than 10 GB/day, size up to a 16 GB or 32 GB plan. Elasticsearch is not CPU-bound -- RAM and disk I/O matter most.
Connect to your server:
ssh root@your-server-ipStep 1: Update and Tune the System
Start with a clean package index and install helpers you will need later:
sudo apt update && sudo apt upgrade -y
sudo apt install -y apt-transport-https ca-certificates curl gnupg wgetElasticsearch uses memory-mapped files heavily. The default Linux limit of 65530 mapped areas per process is too low -- the node will fail its bootstrap checks and refuse to start in production mode. Raise it persistently:
echo 'vm.max_map_count=262144' | sudo tee /etc/sysctl.d/99-elasticsearch.conf
sudo sysctl --systemVerify:
sysctl vm.max_map_countExpected output:
vm.max_map_count = 262144Next, disable swap for the Elasticsearch process. Swapping out heap pages causes garbage-collection pauses that can time out queries:
sudo swapoff -a
sudo sed -i '/ swap / s/^/#/' /etc/fstabIf you cannot disable swap system-wide (for example, on a shared VPS), you will instead set bootstrap.memory_lock: true in elasticsearch.yml later in this guide.
Set the file descriptor and process limits. Elasticsearch ships with a systemd unit that already sets LimitNOFILE=65535 and LimitMEMLOCK=infinity, but confirm:
ulimit -nIf you see 1024, you will need to raise it by editing /etc/security/limits.conf -- but for systemd-managed services the systemd unit values take precedence, which we will verify after install.
Step 2: Add the Elastic APT Repository
Import the Elastic signing key into a dedicated keyring:
curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch \
| sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpgAdd the 8.x stable repository:
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/elastic-8.x.listRefresh the index:
sudo apt updateExpected output (abbreviated):
Get:1 https://artifacts.elastic.co/packages/8.x/apt stable InRelease [10.4 kB]
Get:2 https://artifacts.elastic.co/packages/8.x/apt stable/main amd64 Packages [22.1 kB]
...
Reading package lists... DoneStep 3: Install Elasticsearch
Install the elasticsearch package:
sudo apt install -y elasticsearchThis is the critical moment. During install, Elasticsearch 8 auto-generates TLS certificates, creates a CA, sets a random password for the built-in elastic superuser, and prints an enrollment token for Kibana. The output looks like this:
--------------------------- Security autoconfiguration information ------------------------------Authentication and authorization are enabled. TLS for the transport and HTTP layers is enabled and configured.
The generated password for the elastic built-in superuser is : Rk5z4J*xYq-P8m9WnV2a
If this node should join an existing cluster, you can reconfigure this with '/usr/share/elasticsearch/bin/elasticsearch-reconfigure-node --enrollment-token <token-here>' after creating an enrollment token on your existing cluster.
You can complete the following actions at any time:
Reset the password of the elastic built-in superuser with '/usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic'.
Generate an enrollment token for Kibana instances with '/usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana'.
Generate an enrollment token for Elasticsearch nodes with '/usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s node'.
-------------------------------------------------------------------------------------------------
Save the elastic password to a password manager immediately. It is printed exactly once. If you lose it you can reset it later with elasticsearch-reset-password, but saving it now avoids the extra step.
The installer created the following on disk:
- Binaries:
/usr/share/elasticsearch/ - Config:
/etc/elasticsearch/(includingelasticsearch.yml,jvm.options, auto-generatedcerts/) - Data:
/var/lib/elasticsearch/ - Logs:
/var/log/elasticsearch/ - systemd unit:
/lib/systemd/system/elasticsearch.service - User:
elasticsearch(system user, no login)
Step 4: Configure elasticsearch.yml
Open the main configuration file:
sudo nano /etc/elasticsearch/elasticsearch.ymlFor a single-node production deployment, the relevant sections should look like this:
# ---------------------------------- Cluster ----------------------------------- cluster.name: vpsserver-cluster------------------------------------ Node ------------------------------------
node.name: es-node-1----------------------------------- Paths ------------------------------------
path.data: /var/lib/elasticsearch path.logs: /var/log/elasticsearch---------------------------------- Memory -----------------------------------
bootstrap.memory_lock: true---------------------------------- Network -----------------------------------
network.host: 0.0.0.0 http.port: 9200 transport.port: 9300--------------------------------- Discovery ----------------------------------
Single-node mode skips master election and bootstrap checks on the discovery layer.
discovery.type: single-nodeFor a multi-node cluster, replace the line above with:
discovery.seed_hosts: ["10.0.0.11", "10.0.0.12", "10.0.0.13"]
cluster.initial_master_nodes: ["es-node-1", "es-node-2", "es-node-3"]
--------------------------------- Security -----------------------------------
These are set automatically by the installer in Elasticsearch 8. Confirm they exist:
xpack.security.enabled: true xpack.security.enrollment.enabled: truexpack.security.http.ssl: enabled: true keystore.path: certs/http.p12
xpack.security.transport.ssl: enabled: true verification_mode: certificate keystore.path: certs/transport.p12 truststore.path: certs/transport.p12
Key settings explained:
cluster.name-- All nodes joining the same cluster must share this name. Rename to something meaningful.node.name-- A stable identifier for this node.${HOSTNAME}also works.bootstrap.memory_lock: true-- Tells Elasticsearch to lock its JVM heap into RAM so the kernel will never swap it to disk. RequiresLimitMEMLOCK=infinityin the systemd unit (already set).network.host: 0.0.0.0-- Bind to all interfaces. If you set this to a public IP or0.0.0.0, Elasticsearch activates "production mode" and runs bootstrap checks. For private networks only, you can use the specific internal IP.discovery.type: single-node-- Single-node clusters skip master-election bootstrap checks. For a multi-node cluster, remove this line and usediscovery.seed_hosts+cluster.initial_master_nodesinstead.
bootstrap.memory_lock, enable the systemd override that allows unlimited mlock:sudo systemctl edit elasticsearchAdd the following and save:
[Service]
LimitMEMLOCK=infinityStep 5: Tune the JVM Heap
Elasticsearch runs on a bundled JVM. The single most important performance knob is the heap size.
Rules of thumb:
- Set
XmsandXmxto the same value (prevents runtime resize pauses) - Set heap to ~50% of available RAM, but no more than 32 GB (above ~32 GB you lose compressed object pointers and actually get less addressable memory)
- Leave the other 50% for the Lucene filesystem cache, which is what makes searches fast
Never edit /etc/elasticsearch/jvm.options directly. Drop in an override file:
sudo tee /etc/elasticsearch/jvm.options.d/heap.options > /dev/null <<EOF
-Xms4g
-Xmx4g
EOFSet ownership:
sudo chown root:elasticsearch /etc/elasticsearch/jvm.options.d/heap.options
sudo chmod 660 /etc/elasticsearch/jvm.options.d/heap.optionsReference table for common VPS sizes:
| Total RAM | Recommended -Xms / -Xmx | Use Case |
|---|---|---|
| 4 GB | 2g | Development / small search index |
| 8 GB | 4g | Single-node logs, small app search |
| 12 GB | 4g-6g | CloudCore Professional, production single node |
| 16 GB | 8g | Mid-size logs, observability |
| 32 GB | 16g | Heavy logging / metrics |
| 64 GB | 30g (cap at 30-31g) | Large cluster nodes |
| 128 GB | 30g, run multiple nodes on the same host | Very large data nodes |
Step 6: Start Elasticsearch and Verify
Enable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable elasticsearch
sudo systemctl start elasticsearchElasticsearch takes 20-60 seconds to start on first boot as it initialises its security keystore. Check status:
sudo systemctl status elasticsearchExpected output:
● elasticsearch.service - Elasticsearch
Loaded: loaded (/lib/systemd/system/elasticsearch.service; enabled; preset: enabled)
Active: active (running) since Wed 2026-04-16 10:00:00 UTC; 45s ago
Main PID: 2345 (java)
Tasks: 85 (limit: 14236)
Memory: 4.3G
CPU: 12.345s
CGroup: /system.slice/elasticsearch.service
└─2345 /usr/share/elasticsearch/jdk/bin/java ...Test HTTPS connectivity using the bundled CA certificate (all traffic is TLS-encrypted by default on 8.x):
curl --cacert /etc/elasticsearch/certs/http_ca.crt \
-u elastic \
https://localhost:9200Enter the elastic password when prompted. Expected output:
{
"name" : "es-node-1",
"cluster_name" : "vpsserver-cluster",
"cluster_uuid" : "Lr_g8sX8SPqEJ2FW2qn7Bw",
"version" : {
"number" : "8.14.3",
"build_flavor" : "default",
"build_type" : "deb",
"build_hash" : "...",
"build_date" : "2026-03-14T09:00:00.000000Z",
"build_snapshot" : false,
"lucene_version" : "9.10.0",
"minimum_wire_compatibility_version" : "7.17.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "You Know, for Search"
}Check cluster health:
curl --cacert /etc/elasticsearch/certs/http_ca.crt \
-u elastic \
https://localhost:9200/_cluster/health?prettyExpected:
{
"cluster_name" : "vpsserver-cluster",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 1,
"number_of_data_nodes" : 1,
"active_primary_shards" : 0,
"active_shards" : 0,
...
}Status green on a single-node cluster means everything is healthy.
Step 7: Install and Enroll Kibana
Kibana is the official web UI for Elasticsearch. Install from the same Elastic repo:
sudo apt install -y kibanaGenerate a fresh enrollment token from Elasticsearch:
sudo /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibanaOutput:
eyJ2ZXIiOiI4LjE0LjMiLCJhZHIiOlsiMTkyLjE2OC4xLjEwOjkyMDAiXSwiZmdyIjoiZGVhZGJlZWY...Enroll Kibana using the token (this configures Kibana's kibana.yml automatically with the ES URL, CA fingerprint, and a kibana_system service account token):
sudo /usr/share/kibana/bin/kibana-setup --enrollment-token <PASTE_TOKEN_HERE>Expected output:
Kibana configured successfully.
To start Kibana run: bin/kibana
By default Kibana only binds to localhost. To access it externally, edit /etc/kibana/kibana.yml:
sudo nano /etc/kibana/kibana.ymlSet (or uncomment):
server.port: 5601
server.host: "0.0.0.0"
server.publicBaseUrl: "https://kibana.yourdomain.com"Start and enable the service:
sudo systemctl daemon-reload
sudo systemctl enable kibana
sudo systemctl start kibanaOn first start, Kibana will display a six-digit verification code in the logs. Fetch it:
sudo /usr/share/kibana/bin/kibana-verification-codeOpen http://your-server-ip:5601 in your browser. You will be asked for the verification code, then to log in as elastic with the password from Step 3.
For production, put Kibana behind Nginx with Let's Encrypt -- do not expose port 5601 directly to the internet. A minimal Nginx site:
server { listen 443 ssl http2; server_name kibana.yourdomain.com;ssl_certificate /etc/letsencrypt/live/kibana.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/kibana.yourdomain.com/privkey.pem;
location / { proxy_pass http://127.0.0.1:5601; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
Then set server.host: "127.0.0.1" in kibana.yml to prevent direct external access.
Step 8: Configure Snapshot Repository (S3 / MinIO)
Elasticsearch's only officially supported backup method is snapshots -- consistent, incremental backups of indices to blob storage. Never cp or rsync the data directory on a running cluster.
Install the S3 Repository Plugin
In 8.x the S3 plugin is bundled (no separate install needed) but not auto-loaded. Verify:
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin listIf repository-s3 is not shown, install it:
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install repository-s3Store Credentials in the Keystore
Never put S3 credentials in elasticsearch.yml. Use the keystore:
sudo /usr/share/elasticsearch/bin/elasticsearch-keystore add s3.client.default.access_key
sudo /usr/share/elasticsearch/bin/elasticsearch-keystore add s3.client.default.secret_keyReload the secure settings without restarting:
curl --cacert /etc/elasticsearch/certs/http_ca.crt \
-u elastic \
-X POST https://localhost:9200/_nodes/reload_secure_settingsRegister the Repository
For AWS S3:
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \
-X PUT https://localhost:9200/_snapshot/s3_backup \
-H 'Content-Type: application/json' \
-d '{
"type": "s3",
"settings": {
"bucket": "my-es-snapshots",
"region": "eu-central-1",
"base_path": "prod/es-node-1",
"compress": true
}
}'For MinIO (self-hosted S3-compatible object storage):
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \
-X PUT https://localhost:9200/_snapshot/minio_backup \
-H 'Content-Type: application/json' \
-d '{
"type": "s3",
"settings": {
"bucket": "es-snapshots",
"endpoint": "https://minio.yourdomain.com",
"protocol": "https",
"path_style_access": true,
"compress": true
}
}'Verify the repo is readable:
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \
-X POST https://localhost:9200/_snapshot/s3_backup/_verifySchedule Automatic Snapshots with SLM
Use Snapshot Lifecycle Management (SLM) to run daily snapshots and retain 14 days:
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \
-X PUT https://localhost:9200/_slm/policy/daily-snapshots \
-H 'Content-Type: application/json' \
-d '{
"schedule": "0 30 2 ?",
"name": "<daily-snap-{now/d}>",
"repository": "s3_backup",
"config": {
"indices": ["*"],
"ignore_unavailable": false,
"include_global_state": true
},
"retention": {
"expire_after": "14d",
"min_count": 5,
"max_count": 30
}
}'Trigger the policy once manually to confirm it works:
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \
-X POST https://localhost:9200/_slm/policy/daily-snapshots/_executeYou can now browse snapshots in Kibana under Stack Management -> Snapshot and Restore.
Step 9: Apply Index Lifecycle Management (ILM) Policies
For time-series data (logs, metrics, events) you do not want every index kept at full performance forever. Index Lifecycle Management (ILM) automates moving old data through phases:
- Hot -- Active writes, high-performance storage
- Warm -- Read-only, moved to cheaper storage, force-merged
- Cold -- Rarely queried, aggressively compressed, searchable snapshots
- Frozen -- Offloaded to blob storage
- Delete -- Gone
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \
-X PUT https://localhost:9200/_ilm/policy/logs-policy \
-H 'Content-Type: application/json' \
-d '{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_primary_shard_size": "30gb",
"max_age": "7d"
},
"set_priority": { "priority": 100 }
}
},
"warm": {
"min_age": "7d",
"actions": {
"forcemerge": { "max_num_segments": 1 },
"shrink": { "number_of_shards": 1 },
"set_priority": { "priority": 50 }
}
},
"cold": {
"min_age": "30d",
"actions": {
"searchable_snapshot": { "snapshot_repository": "s3_backup" },
"set_priority": { "priority": 0 }
}
},
"delete": {
"min_age": "90d",
"actions": { "delete": {} }
}
}
}
}'Attach the policy to an index template so every new logs-* index inherits it:
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \
-X PUT https://localhost:9200/_index_template/logs-template \
-H 'Content-Type: application/json' \
-d '{
"index_patterns": ["logs-*"],
"data_stream": {},
"template": {
"settings": {
"index.lifecycle.name": "logs-policy",
"index.lifecycle.rollover_alias": "logs",
"number_of_shards": 1,
"number_of_replicas": 0
}
}
}'New documents indexed into the logs data stream now automatically rotate through the hot/warm/cold/delete phases without any intervention.
Step 10: Harden and Secure
Firewall: Never Expose 9200 / 9300 to the Internet
The HTTP API (9200) and transport layer (9300) should never be reachable from the public internet, even with authentication. Use UFW to only allow them from localhost and any trusted app servers:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 443/tcp # Kibana via Nginx
sudo ufw allow from 10.0.0.0/24 to any port 9200 # internal app network only
sudo ufw enableCreate Non-Superuser Accounts
Never use the elastic superuser for applications. Create role-scoped users:
# Create a user that can read/write a single index pattern curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \ -X POST https://localhost:9200/_security/role/app_writer \ -H 'Content-Type: application/json' \ -d '{ "indices": [ { "names": ["app-logs-*"], "privileges": ["create_index", "write", "read", "view_index_metadata"] } ] }'
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \ -X POST https://localhost:9200/_security/user/app_writer \ -H 'Content-Type: application/json' \ -d '{ "password": "use-a-long-random-password", "roles": ["app_writer"], "full_name": "Application log writer" }'
Rotate the elastic Password Periodically
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elasticMonitor Audit Logs
Enable audit logging in elasticsearch.yml if you need SOC 2 / ISO 27001 compliance:
xpack.security.audit.enabled: true
xpack.security.audit.logfile.events.include: ["access_denied","authentication_failed","access_granted"]Audit events will be written to /var/log/elasticsearch/vpsserver-cluster_audit.json.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
Service exits immediately with max virtual memory areas vm.max_map_count [65530] is too low | Kernel sysctl not raised | Re-run echo 'vm.max_map_count=262144' \</td><td>sudo tee /etc/sysctl.d/99-elasticsearch.conf && sudo sysctl --system |
bootstrap check failure: memory locking requested but memory is not locked | bootstrap.memory_lock: true without matching LimitMEMLOCK=infinity | Run sudo systemctl edit elasticsearch and add [Service]\nLimitMEMLOCK=infinity, then sudo systemctl daemon-reload && sudo systemctl restart elasticsearch |
received plaintext http traffic on an https channel | Client using http:// instead of https:// | Elasticsearch 8 is HTTPS-only by default. Use https:// with --cacert /etc/elasticsearch/certs/http_ca.crt |
authentication failed with correct password | Password has shell metacharacters that got expanded | Wrap password in single quotes or use -u elastic without a password and let curl prompt |
cluster health: yellow on single-node | Default template wants 1 replica but there is no second node | Set number_of_replicas: 0 in your index template, or ignore it -- yellow is functional |
| Kibana: "Kibana server is not ready yet" | Kibana cannot reach ES (cert fingerprint mismatch) | Re-run kibana-setup --enrollment-token <new-token> after generating a fresh token |
| High heap usage, frequent GC pauses | Heap too small or over 32 GB (loses compressed oops) | Tune Xms/Xmx to 50% of RAM, max 30-31 GB |
| Slow queries, high disk I/O | Lucene cache too small | Lower the JVM heap to leave more RAM for the kernel page cache |
Snapshot fails with AccessDenied | S3 credentials in keystore are wrong, or bucket policy denies writes | Reset keys with elasticsearch-keystore add and reload secure settings |
Viewing Logs
Real-time log stream:
sudo journalctl -u elasticsearch -fStructured JSON logs:
sudo tail -f /var/log/elasticsearch/vpsserver-cluster.logKibana logs:
sudo journalctl -u kibana -fFAQ
Is Elasticsearch free to self-host on my own VPS?
Yes. Elasticsearch 8 is dual-licensed under SSPL v1 and Elastic License v2. The default Basic tier -- which includes TLS, RBAC, snapshot lifecycle management, canvas, maps, and the dense_vector field type -- is free to run on your own hardware forever. Only offering Elasticsearch itself as a managed third-party SaaS, or embedding it in a competing product, requires a commercial Elastic subscription. For almost every real-world use case (powering search in your product, running logs for your team, building a RAG pipeline), the Basic license is all you need.
How much RAM does Elasticsearch really need?
For a working single-node production setup we recommend at least 8 GB -- 4 GB for the JVM heap and 4 GB left for the Lucene filesystem cache that keeps hot segments in memory. Development and small indices run fine on 4 GB. For logging workloads receiving 10 GB+ per day of data, start at 16 GB and scale up from there. The most common mistake is setting the JVM heap too high (above 50% of RAM, or above 32 GB), which starves the Lucene cache and actually hurts performance.
Should I pick Elasticsearch or OpenSearch?
OpenSearch is an Apache 2.0 fork of Elasticsearch 7.10 maintained by AWS, and it tracks a different release line. Pick OpenSearch if you need strictly permissive licensing, are deploying on AWS and want deep integration with AWS-managed OpenSearch Service, or if you plan to offer search-as-a-service to third parties. Pick Elasticsearch for better Kibana visualizations, newer ML features, more mature vector search, and a faster release cadence. For most logging and app-search use cases, either will work.
How do Elasticsearch snapshots compare to filesystem backups?
Elasticsearch snapshots are the only supported backup method. They are consistent (even while the cluster is writing), incremental (only new segments are uploaded), deduplicated at the segment level, and can be restored to a different cluster or a different version. Copying the data directory with cp/rsync on a running cluster will eventually produce a corrupt backup because Lucene segments are being mutated concurrently. Use the built-in snapshot machinery -- to S3, MinIO, Azure Blob, GCS, or a shared filesystem -- and schedule it with SLM as shown in Step 8.
Can Elasticsearch replace my log aggregation stack?
Yes, and this is one of the most common self-hosted deployments. The combination of Filebeat (on each server, tails log files) -> Elasticsearch (storage + search) -> Kibana (dashboards and alerting) replaces commercial offerings like Datadog Logs or Splunk. For Kubernetes, use Fluent Bit or the Elastic Agent. If you want a lighter-weight logging stack without Lucene overhead, compare with Grafana Loki or Graylog -- Loki is cheaper at scale for pure log storage, while Graylog sits between Elasticsearch and Loki in features and cost.
What is the difference between a data stream and an index?
A data stream is an abstraction over a sequence of auto-rolling backing indices. You write to the data stream name (e.g. logs), and Elasticsearch internally routes writes to the latest backing index (e.g. .ds-logs-000005). When ILM triggers a rollover, a new backing index is created and writes switch to it automatically. Use data streams for append-only time-series data (logs, metrics, traces). Use plain indices for data you mutate in place (product catalogs, user profiles).
Next Steps
Now that Elasticsearch and Kibana are running on your VPS, here are recommended next steps:
- Install Filebeat to ship logs -- Deploy Filebeat on your application servers and point it at this cluster. Filebeat auto-discovers Nginx, Apache, Docker and Kubernetes logs and parses them into structured fields.
- Compare alternatives -- If you decide Elasticsearch is too resource-heavy, check our guides on how to install OpenSearch (Apache 2.0 fork), how to install Graylog (purpose-built log aggregation UI), or how to install Loki (Prometheus-style label-indexed logs at a fraction of the disk cost).
- Build a RAG pipeline with
dense_vector-- Elasticsearch 8's native HNSW index ondense_vectorfields makes it a solid production-grade vector store. Combine it with Ollama embeddings to build retrieval-augmented question answering over your own documents.
- Set up alerting -- Kibana's alerting framework can trigger webhooks, Slack messages or PagerDuty incidents when log patterns match. Start with a simple "more than 10 5xx errors in 5 minutes" rule.
- Read the official docs -- The Elasticsearch documentation is extensive and canonical. Bookmark the Query DSL reference and the Index Settings page.
Need More Horsepower for Your Cluster?>
Elasticsearch's performance scales directly with RAM and disk I/O. Our CloudCore Professional plan gives you 12 GB RAM, 6 vCPU and 100 GB NVMe for EUR 19.99/month -- enough for a production single-node cluster serving app search or 50 GB/day of logs. For multi-node clusters or heavier logging workloads, scale to our 16 GB and 32 GB plans on the same private network so nodes communicate over the internal interface with no bandwidth charges.>
Deploy your VPS now and get Elasticsearch running in the next 40 minutes.