How to Deploy Elasticsearch – Search & Analytics Engine

Updated on 12 December, 2025
Deploy Elasticsearch with Docker Compose on Ubuntu 24.04 using Traefik for secure log management.
How to Deploy Elasticsearch – Search & Analytics Engine header image

Elasticsearch is a distributed search and analytics engine built on Apache Lucene. It provides near real-time search capabilities for structured and unstructured data, making it ideal for log aggregation, full-text search, and business analytics. Elasticsearch scales horizontally across multiple nodes and supports complex queries through its RESTful API.

In this article, you will deploy Elasticsearch using Docker Compose, configure persistent storage for indexed data, and set up Traefik as a reverse proxy to securely access your Elasticsearch instance.

Prerequisites

Before you begin, you need to:

Set Up the Directory Structure and Environment Variables

In this section, you prepare the required directory structure for Elasticsearch and define environment variables in a .env file.

  1. Create the directory structure for Elasticsearch.

    console
    $ mkdir -p ~/elasticsearch-logging/{elasticsearch-data,elasticsearch-config}
    

    These directories serve different purposes:

    • elasticsearch-data: Stores indexed data, including shards and segments.
    • elasticsearch-config: Contains YAML configuration files.
  2. Navigate into the elasticsearch-logging directory.

    console
    $ cd ~/elasticsearch-logging
    
  3. Set proper ownership for the Elasticsearch data directory. Elasticsearch runs as the elasticsearch user (UID 1000) inside the container.

    console
    $ sudo chown -R 1000:1000 elasticsearch-data
    

    You can verify this UID by running docker run --rm elasticsearch:8.17.0 id.

  4. Create a .env file.

    console
    $ nano .env
    

    Add the following variables:

    ini
    DOMAIN=elasticsearch.example.com
    LETSENCRYPT_EMAIL=admin@example.com
    

    Replace:

    • elasticsearch.example.com with your domain.
    • admin@example.com with your email.

    Save and close the file.

  5. Create an Elasticsearch configuration file.

    console
    $ nano elasticsearch-config/elasticsearch.yml
    
  6. Add the following content.

    yaml
    cluster.name: logging-cluster
    node.name: node-1
    network.host: 0.0.0.0
    http.port: 9200
    discovery.type: single-node
    xpack.security.enabled: false
    

    Save and close the file.

    The configuration includes:

    • cluster.name: Identifies this Elasticsearch cluster for node discovery.
    • node.name: Assigns a unique identifier to this node.
    • network.host: Configures Elasticsearch to listen on all network interfaces, allowing Traefik to connect from the Docker network.
    • discovery.type: Configures single-node mode, disabling cluster formation requirements.
    • xpack.security.enabled: Disables authentication for simplified initial deployment.

Deploy with Docker Compose

In this section, you create and deploy the Docker Compose stack that runs Elasticsearch behind Traefik. Docker Compose manages both containers, applies the environment variables from your .env file, and automatically configures HTTPS routing through Traefik.

  1. Create a new Docker Compose manifest.

    console
    $ nano docker-compose.yaml
    
  2. Add the following content.

    yaml
    services:
      traefik:
        image: traefik:v3.6
        container_name: traefik
        command:
          - "--providers.docker=true"
          - "--providers.docker.exposedbydefault=false"
          - "--entrypoints.web.address=:80"
          - "--entrypoints.websecure.address=:443"
          - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
          - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
          - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
          - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
          - "--certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}"
          - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - "letsencrypt:/letsencrypt"
          - "/var/run/docker.sock:/var/run/docker.sock:ro"
        restart: unless-stopped
    
      elasticsearch:
        image: docker.elastic.co/elasticsearch/elasticsearch:9.2.0
        container_name: elasticsearch
        hostname: elasticsearch
        expose:
          - "9200"
        volumes:
          - "./elasticsearch-config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml"
          - "./elasticsearch-data:/usr/share/elasticsearch/data"
        environment:
          - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.elasticsearch.rule=Host(`${DOMAIN}`)"
          - "traefik.http.routers.elasticsearch.entrypoints=websecure"
          - "traefik.http.routers.elasticsearch.tls.certresolver=letsencrypt"
        restart: unless-stopped
    
    volumes:
      letsencrypt:
    

    Save and close the file.

    This manifest specifies:

    • services: Defines two containers working together:
      • traefik: Accepts external connections, provisions SSL/TLS certificates automatically, and routes requests to backend services.
      • elasticsearch: Runs the core search engine, indexes documents, and responds to queries.
    • image: Specifies container images from Docker Hub and the official Elastic registry.
    • container_name: Assigns fixed names for easier log inspection and container management.
    • command (Traefik): Configures Docker provider integration, HTTP/HTTPS entry points on ports 80 and 443, forced HTTPS redirects, and automated Let's Encrypt certificate generation using HTTP challenge validation.
    • ports (Traefik): Binds ports 80 (HTTP) and 443 (HTTPS) on the host to receive incoming traffic.
    • expose (Elasticsearch): Makes port 9200 accessible only within the Docker network, preventing direct public access.
    • volumes:
      • Local directories (./elasticsearch-config, ./elasticsearch-data) mount into containers to persist configuration and search indexes.
      • The letsencrypt named volume stores certificate data permanently.
      • Mounting /var/run/docker.sock grants Traefik read-only access for automatic service discovery.
    • environment (Elasticsearch): Sets JVM memory limits to 512MB minimum and maximum for stable resource usage.
    • labels (Elasticsearch): Traefik routing rules that activate the reverse proxy, match requests by hostname, and apply Let's Encrypt TLS certificates.
    • restart: unless-stopped: Automatically restarts containers after crashes or server reboots, except when manually stopped.
  3. Create and start the services.

    console
    $ docker compose up -d
    
  4. Verify that the services are running.

    console
    $ docker compose ps
    

    Output:

    NAME            IMAGE                                                  COMMAND                  SERVICE         CREATED          STATUS          PORTS
    elasticsearch   docker.elastic.co/elasticsearch/elasticsearch:9.2.0    "/bin/tini -- /usr/l…"   elasticsearch   22 seconds ago   Up 21 seconds   9200/tcp
    traefik         traefik:v3.6                                           "/entrypoint.sh --pr…"   traefik         22 seconds ago   Up 21 seconds   0.0.0.0:80->80/tcp, [::]:80->80/tcp, 0.0.0.0:443->443/tcp, [::]:443->443/tcp

    Both containers are running. Elasticsearch processes search requests while Traefik handles incoming connections on ports 80 and 443.

  5. View the logs of the services.

    console
    $ docker compose logs
    

    For more information on managing a Docker Compose stack, see the How To Use Docker Compose article.

Access Elasticsearch

This section demonstrates verification of your Elasticsearch installation through cluster health checks and test index creation to validate search and storage functionality.

  1. Access the Elasticsearch API in your browser.

    https://elasticsearch.example.com

    You should see a JSON response with cluster information including the version number and cluster UUID.

  2. Check cluster health using the command line.

    console
    $ curl https://elasticsearch.example.com/_cluster/health?pretty
    

    Output:

    {
      "cluster_name" : "logging-cluster",
      "status" : "green",
      "number_of_nodes" : 1,
      "number_of_data_nodes" : 1
    }

    A green status indicates the cluster is healthy and operational.

  3. Create a test index to verify data storage.

    console
    $ curl -X PUT "https://elasticsearch.example.com/test-index" -H 'Content-Type: application/json' -d'
    {
      "settings": {
        "number_of_shards": 1,
        "number_of_replicas": 0
      }
    }'
    

    Output:

    {"acknowledged":true,"shards_acknowledged":true,"index":"test-index"}
  4. Index a sample document.

    console
    $ curl -X POST "https://elasticsearch.example.com/test-index/_doc" -H 'Content-Type: application/json' -d'
    {
      "message": "Hello Elasticsearch",
      "timestamp": "2025-12-01T19:32:43Z"
    }'
    
  5. Search for the document.

    console
    $ curl "https://elasticsearch.example.com/test-index/_search?pretty"
    

    The output displays your indexed document, confirming that Elasticsearch can store and retrieve data successfully.

Conclusion

You have successfully deployed Elasticsearch for log management with HTTPS encryption. This Docker Compose implementation combines the search engine with an automatic SSL reverse proxy, while volume mounts preserve indexed data through container updates and restarts. Let's Encrypt integration maintains valid certificates automatically as Traefik routes incoming requests securely. Your Elasticsearch instance now stands ready to ingest application logs, perform full-text searches across massive datasets, and deliver real-time analytics through its RESTful API.

Comments