Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Elasticsearch Ubuntu
GUIDEInstall Guides

How to Install Elasticsearch 8 on Ubuntu 24.04 VPS: Search and Analytics Engine

27 min read

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?
  • Why Self-Host Elasticsearch vs. Elastic Cloud?
  • Licensing: SSPL and the Elastic License v2
  • Prerequisites
  • Step 1: Update and Tune the System
  • Step 2: Add the Elastic APT Repository
  • Step 3: Install Elasticsearch
  • Step 4: Configure elasticsearch.yml
  • Step 5: Tune the JVM Heap
  • Step 6: Start Elasticsearch and Verify
  • Step 7: Install and Enroll Kibana
  • Step 8: Configure Snapshot Repository (S3 / MinIO)
  • Step 9: Apply Index Lifecycle Management (ILM) Policies
  • Step 10: Harden and Secure
  • Troubleshooting
  • FAQ
  • Next Steps
  • 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-text or OpenAI text-embedding-3
    • Business analytics -- high-cardinality aggregations over billions of events that are impractical in OLTP databases
    • Geospatial search -- geo_point and geo_shape queries for maps, delivery, logistics
    Elasticsearch 8 introduced deep changes worth knowing about: security (TLS + authentication + RBAC) is enabled by default on fresh installs, the JVM is bundled (no separate Java install required), native vector search (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.
    Elastic Cloud makes sense when your team has no Linux operations capacity. For everyone else, self-hosting on a well-provisioned VPS is significantly cheaper and more flexible.

    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)
    What this means in practice:

    • 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_vector field 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.
    For 99% of users -- anyone running Elasticsearch to power search or analytics inside their own product -- the license change has zero practical impact. If you are building a competing managed search service, consider OpenSearch instead, which is an Apache 2.0 fork of Elasticsearch 7.10 maintained by AWS.

    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:

    bash
    ssh root@your-server-ip

    Step 1: Update and Tune the System

    Start with a clean package index and install helpers you will need later:

    bash
    sudo apt update && sudo apt upgrade -y
    sudo apt install -y apt-transport-https ca-certificates curl gnupg wget

    Elasticsearch 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:

    bash
    echo 'vm.max_map_count=262144' | sudo tee /etc/sysctl.d/99-elasticsearch.conf
    sudo sysctl --system

    Verify:

    bash
    sysctl vm.max_map_count

    Expected output:

    text
    vm.max_map_count = 262144

    Next, disable swap for the Elasticsearch process. Swapping out heap pages causes garbage-collection pauses that can time out queries:

    bash
    sudo swapoff -a
    sudo sed -i '/ swap / s/^/#/' /etc/fstab

    If 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:

    bash
    ulimit -n

    If 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:

    bash
    curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch \
      | sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg

    Add the 8.x stable repository:

    bash
    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.list

    Refresh the index:

    bash
    sudo apt update

    Expected output (abbreviated):

    text
    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... Done

    Step 3: Install Elasticsearch

    Install the elasticsearch package:

    bash
    sudo apt install -y elasticsearch

    This 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:

    text
    --------------------------- 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/ (including elasticsearch.yml, jvm.options, auto-generated certs/)
    • 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:

    bash
    sudo nano /etc/elasticsearch/elasticsearch.yml

    For a single-node production deployment, the relevant sections should look like this:

    yaml
    # ---------------------------------- 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-node

    For 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: true

    xpack.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. Requires LimitMEMLOCK=infinity in the systemd unit (already set).
    • network.host: 0.0.0.0 -- Bind to all interfaces. If you set this to a public IP or 0.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 use discovery.seed_hosts + cluster.initial_master_nodes instead.
    If you enabled bootstrap.memory_lock, enable the systemd override that allows unlimited mlock:

    bash
    sudo systemctl edit elasticsearch

    Add the following and save:

    ini
    [Service]
    LimitMEMLOCK=infinity

    Step 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 Xms and Xmx to 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
    Example for a 12 GB RAM VPS: 4 GB heap (leaving ~7 GB for Lucene cache and ~1 GB for OS + Kibana).

    Never edit /etc/elasticsearch/jvm.options directly. Drop in an override file:

    bash
    sudo tee /etc/elasticsearch/jvm.options.d/heap.options > /dev/null <<EOF
    -Xms4g
    -Xmx4g
    EOF

    Set ownership:

    bash
    sudo chown root:elasticsearch /etc/elasticsearch/jvm.options.d/heap.options
    sudo chmod 660 /etc/elasticsearch/jvm.options.d/heap.options

    Reference table for common VPS sizes:

    Total RAMRecommended -Xms / -XmxUse Case
    4 GB2gDevelopment / small search index
    8 GB4gSingle-node logs, small app search
    12 GB4g-6gCloudCore Professional, production single node
    16 GB8gMid-size logs, observability
    32 GB16gHeavy logging / metrics
    64 GB30g (cap at 30-31g)Large cluster nodes
    128 GB30g, run multiple nodes on the same hostVery large data nodes

    Step 6: Start Elasticsearch and Verify

    Enable and start the service:

    bash
    sudo systemctl daemon-reload
    sudo systemctl enable elasticsearch
    sudo systemctl start elasticsearch

    Elasticsearch takes 20-60 seconds to start on first boot as it initialises its security keystore. Check status:

    bash
    sudo systemctl status elasticsearch

    Expected output:

    text
    ● 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):

    bash
    curl --cacert /etc/elasticsearch/certs/http_ca.crt \
      -u elastic \
      https://localhost:9200

    Enter the elastic password when prompted. Expected output:

    json
    {
      "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:

    bash
    curl --cacert /etc/elasticsearch/certs/http_ca.crt \
      -u elastic \
      https://localhost:9200/_cluster/health?pretty

    Expected:

    json
    {
      "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:

    bash
    sudo apt install -y kibana

    Generate a fresh enrollment token from Elasticsearch:

    bash
    sudo /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana

    Output:

    text
    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):

    bash
    sudo /usr/share/kibana/bin/kibana-setup --enrollment-token <PASTE_TOKEN_HERE>

    Expected output:

    text
    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:

    bash
    sudo nano /etc/kibana/kibana.yml

    Set (or uncomment):

    yaml
    server.port: 5601
    server.host: "0.0.0.0"
    server.publicBaseUrl: "https://kibana.yourdomain.com"

    Start and enable the service:

    bash
    sudo systemctl daemon-reload
    sudo systemctl enable kibana
    sudo systemctl start kibana

    On first start, Kibana will display a six-digit verification code in the logs. Fetch it:

    bash
    sudo /usr/share/kibana/bin/kibana-verification-code

    Open 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:

    nginx
    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:

    bash
    sudo /usr/share/elasticsearch/bin/elasticsearch-plugin list

    If repository-s3 is not shown, install it:

    bash
    sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install repository-s3

    Store Credentials in the Keystore

    Never put S3 credentials in elasticsearch.yml. Use the keystore:

    bash
    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_key

    Reload the secure settings without restarting:

    bash
    curl --cacert /etc/elasticsearch/certs/http_ca.crt \
      -u elastic \
      -X POST https://localhost:9200/_nodes/reload_secure_settings

    Register the Repository

    For AWS S3:

    bash
    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):

    bash
    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:

    bash
    curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \
      -X POST https://localhost:9200/_snapshot/s3_backup/_verify

    Schedule Automatic Snapshots with SLM

    Use Snapshot Lifecycle Management (SLM) to run daily snapshots and retain 14 days:

    bash
    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:

    bash
    curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \
      -X POST https://localhost:9200/_slm/policy/daily-snapshots/_execute

    You 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
    Create a policy suitable for application logs:

    bash
    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:

    bash
    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:

    bash
    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 enable

    Create Non-Superuser Accounts

    Never use the elastic superuser for applications. Create role-scoped users:

    bash
    # 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

    bash
    sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic

    Monitor Audit Logs

    Enable audit logging in elasticsearch.yml if you need SOC 2 / ISO 27001 compliance:

    yaml
    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

    ProblemCauseSolution
    Service exits immediately with max virtual memory areas vm.max_map_count [65530] is too lowKernel sysctl not raisedRe-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 lockedbootstrap.memory_lock: true without matching LimitMEMLOCK=infinityRun 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 channelClient 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 passwordPassword has shell metacharacters that got expandedWrap password in single quotes or use -u elastic without a password and let curl prompt
    cluster health: yellow on single-nodeDefault template wants 1 replica but there is no second nodeSet 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 pausesHeap 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/OLucene cache too smallLower the JVM heap to leave more RAM for the kernel page cache
    Snapshot fails with AccessDeniedS3 credentials in keystore are wrong, or bucket policy denies writesReset keys with elasticsearch-keystore add and reload secure settings

    Viewing Logs

    Real-time log stream:

    bash
    sudo journalctl -u elasticsearch -f

    Structured JSON logs:

    bash
    sudo tail -f /var/log/elasticsearch/vpsserver-cluster.log

    Kibana logs:

    bash
    sudo journalctl -u kibana -f

    FAQ

    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 on dense_vector fields 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.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket