Exploring ECK & Elasticsearch

A step-by-step guide to Elasticsearch administration, Kubernetes orchestration with ECK, data ingestion, and certification preparation.

Certification Prep ECK Operator Hands-On Labs

What is Elasticsearch?

Elasticsearch is a distributed, RESTful search and analytics engine built on top of Apache LuceneApache Lucene is a high-performance, full-text search library written in Java. Elasticsearch wraps Lucene and adds distribution, a REST API, JSON-based queries, and cluster management on top of it. You never interact with Lucene directly — ES handles that for you.. It stores data as JSON documents, indexes them in near real-time, and lets you run powerful searches, aggregations, and analytics across massive datasets.

📄
Your Data
Logs, metrics, documents, events
Elasticsearch
Index, store, search, analyze
📊
Kibana
Visualize, dashboard, explore
💡 Key Insight Think of Elasticsearch as a database optimized for search. Where traditional databases are great at storing and retrieving by ID, Elasticsearch excels at full-text search, fuzzy matching, and analytical aggregations across millions of records in milliseconds.

Why Use Elasticsearch?

🔍 Search

Full-text search with relevance scoring, fuzzy matching, autocomplete, and support for dozens of languages and analyzers.

📈 Observability

Store and query logs, metrics, and traces. The Elastic Stack (ELK) is the backbone of many observability platforms worldwide.

🛡️ Security Analytics

SIEM capabilities, threat detection, anomaly detection, and compliance auditing powered by real-time data indexing.

The Elastic Stack at a Glance

ComponentRoleWhat It Does
ElasticsearchStorage & Search EngineIndexes, stores, and retrieves JSON documents. The core brain.
KibanaVisualization UIDashboards, Dev Tools console, index management, and more.
LogstashData PipelineIngests data from many sources, transforms it, and ships to ES.
Beats / Elastic AgentLightweight ShippersCollect data at the edge — Filebeat for logs, Metricbeat for metrics, etc.

Core Concepts

Before diving into administration, you need to internalize these building blocks. They come up repeatedly in the certification exam.

Cluster

A cluster is a collection of one or more nodes that together hold your entire data and provide federated indexing and search capabilities. Every cluster has a unique name (default: elasticsearch). A node joins a cluster by its cluster name.

Node

A single running instance of Elasticsearch. Nodes can have different rolesNode Roles:
master — manages cluster state
data — holds shards, runs queries
ingest — runs ingest pipelines
ml — machine learning
coordinating — routes requests
transform — runs transforms

In production, you typically separate master-eligible nodes from data nodes for stability.
: master, data, ingest, coordinating, and more.

Index

An index is a collection of documents that share similar characteristics. It's analogous to a "database" in relational terms. Each index has a mappingMappings define the schema for documents in an index: which fields exist, their data types (text, keyword, integer, geo_point, etc.), and how they should be analyzed. Mappings can be explicit (you define them) or dynamic (ES infers types automatically). that defines the structure of documents within it.

Document

The basic unit of information. A JSON object that gets indexed. For example, a single log entry, a product record, or a user profile. Each document lives in an index and has a unique _id.

Shards & Replicas

An index is split into primary shards distributed across nodes. Each primary shard can have zero or more replica shards for fault tolerance and read throughput. By default, an index has 1 primary shard and 1 replica.

🎓 Certification Note You'll be tested on configuring shard counts, understanding shard allocation, and knowing the impact of shard sizing on cluster performance. Remember: you cannot change the number of primary shards after index creation (without reindexing).
Inverted Index

The data structure that makes search fast. Instead of mapping documents → words, it maps words → documents. When you search for "kubernetes", Elasticsearch looks up the term in the inverted index and instantly knows which documents contain it.

Kubernetes Basics

To use ECK, you need a working understanding of Kubernetes. Here's a crash course on the concepts that matter most for running Elasticsearch.

🏗️
Control Plane
API Server, etcd, Scheduler, Controller
📦
Worker Nodes
kubelet, kube-proxy, container runtime
🐳
Pods
Your running containers

Key Kubernetes Objects for Elasticsearch

ObjectWhat It IsWhy It Matters for ES
PodSmallest deployable unit; wraps one or more containersEach ES node runs as a Pod
StatefulSetManages pods with stable identity and persistent storageES is stateful — ECK creates StatefulSets under the hood
ServiceStable network endpoint for a set of podsExposes ES cluster API (port 9200) and inter-node transport (9300)
PersistentVolumeClaimRequest for storageEach ES data node needs persistent disk for indices
ConfigMap / SecretConfiguration and sensitive dataES passwords, TLS certs, elasticsearch.yml overrides
NamespaceVirtual cluster within a clusterIsolate your Elastic stack from other workloads
CRDCustom Resource Definition (CRD)
CRDs extend the Kubernetes API with new object types. ECK registers CRDs like Elasticsearch, Kibana, Beat, etc. so you can manage Elastic resources with simple YAML manifests just like native K8s objects.
Extends K8s API with custom objectsECK adds Elasticsearch, Kibana, and Agent CRDs
OperatorOperator Pattern
An operator is a controller that watches custom resources and automates application-specific operations (deploy, upgrade, backup, scale). The ECK operator watches your Elasticsearch CRDs and automatically manages StatefulSets, Services, TLS certificates, users, and rolling upgrades.
Application-aware controllerECK operator manages the entire ES lifecycle

Local Setup with Docker

The fastest way to learn is to run Elasticsearch locally. Below are two paths: a simple Docker Compose setup (no K8s) for quick exploration, and a K3d/Kind approach for practicing with ECK.

This gets Elasticsearch + Kibana running in under 2 minutes. Great for learning queries, mappings, and ingestion.

1

Create docker-compose.yml

# docker-compose.yml
version: "3.8"
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.17.0
    container_name: es-node
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms1g -Xmx1g"
    ports:
      - "9200:9200"
    volumes:
      - es-data:/usr/share/elasticsearch/data

  kibana:
    image: docker.elastic.co/kibana/kibana:8.17.0
    container_name: kibana
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch

volumes:
  es-data:
    driver: local
2

Start the Stack

# Start in detached mode
docker compose up -d

# Verify Elasticsearch is running
curl http://localhost:9200

# Open Kibana in your browser
# http://localhost:5601
3

Verify It Works

# You should see a JSON response with cluster info:
# {
#   "name": "es-node",
#   "cluster_name": "docker-cluster",
#   "version": { "number": "8.17.0", ... },
#   "tagline": "You Know, for Search"
# }

# Check cluster health
curl http://localhost:9200/_cluster/health?pretty
⚠️ Requirements Docker Desktop must be installed with at least 4GB of memory allocated. On Linux, you may need to run sudo sysctl -w vm.max_map_count=262144 for Elasticsearch to start properly.

For practicing ECK and Kubernetes-based Elasticsearch management. This mirrors a real production setup on your laptop.

1

Install Prerequisites

# Install k3d (lightweight K8s using Docker)
curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash

# Install kubectl
# macOS:
brew install kubectl
# Linux:
curl -LO "https://dl.k8s.io/release/$(curl -L -s \
  https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl && sudo mv kubectl /usr/local/bin/

# Install Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
2

Create a K3d Cluster

# Create cluster with enough memory
k3d cluster create elastic-lab \
  --agents 2 \
  --port "9200:9200@loadbalancer" \
  --port "5601:5601@loadbalancer"

# Verify
kubectl get nodes
3

Install ECK Operator

# Install ECK CRDs
kubectl create -f https://download.elastic.co/downloads/eck/2.14.0/crds.yaml

# Install ECK Operator
kubectl apply -f https://download.elastic.co/downloads/eck/2.14.0/operator.yaml

# Watch the operator come up
kubectl -n elastic-system logs -f statefulset.apps/elastic-operator
💡 Alternative: Kind or Minikube If you prefer, you can use kind create cluster or minikube start --memory=8192 instead of k3d. The ECK installation steps remain the same.

kubectl Cheatsheet

The essential commands you'll use daily when managing Elasticsearch on Kubernetes. Bookmark this section.

Core Commands

# Get resources
kubectl get pods                          # List pods
kubectl get pods -o wide                  # Pods with node info
kubectl get pods,services,configmaps      # Multiple types
kubectl get elasticsearch                 # ECK custom resources

# Describe (detailed info)
kubectl describe pod elasticsearch-es-main-0

# Logs
kubectl logs elasticsearch-es-main-0              # Current logs
kubectl logs elasticsearch-es-main-0 -f           # Stream / follow
kubectl logs elasticsearch-es-main-0 -c elasticsearch  # Specific container

# Events (scheduler decisions)
kubectl get events --sort-by=.metadata.creationTimestamp

# Exec into a pod
kubectl exec -it elasticsearch-es-main-0 -c elasticsearch -- bash

# Port forward
kubectl port-forward service/quickstart-es-http 9200
kubectl port-forward service/quickstart-kb-http 5601

# Copy files
kubectl cp my-pod:/tmp/data.json ./local-data.json

# Resource usage
kubectl top pods
kubectl top nodes

# Context management
kubectl config current-context
kubectl config use-context elastic-lab
kubectl config set-context --current --namespace=elastic-system

ECK-Specific Commands

# Check ECK operator logs
kubectl -n elastic-system logs -f statefulset.apps/elastic-operator

# List all Elastic resources
kubectl get elasticsearch,kibana,beat,agent --all-namespaces

# Get the elastic user password
kubectl get secret quickstart-es-elastic-user -o go-template='{{.data.elastic | base64decode}}'

# Check cluster health via API
kubectl port-forward svc/quickstart-es-http 9200 &
curl -u "elastic:$PASSWORD" -k https://localhost:9200/_cluster/health?pretty

# Explain a custom resource
kubectl explain elasticsearch.spec.nodeSets

Welcome to Exploring ECK!

Elastic Cloud on Kubernetes (ECK) is the official Kubernetes operator from Elastic. Built on the Operator PatternThe Operator Pattern
Operators encode human operational knowledge into software. Instead of an admin manually running upgrades, scaling nodes, and configuring TLS, the ECK operator watches your desired state (YAML) and continuously reconciles the actual cluster to match it. It handles rolling upgrades, certificate rotation, user management, and more — automatically.
, it automates deploying, managing, and operating the entire Elastic Stack on Kubernetes.

What ECK Automates

Deployment & Upgrades — Rolling upgrades with zero downtime. Change the version number in YAML and ECK handles the rest.

TLS Everywhere — Auto-generates and rotates self-signed certificates for HTTP and transport layers.

User Management — Creates the elastic superuser and stores credentials in Kubernetes Secrets.

Scaling — Add or remove nodes by changing a number. ECK rebalances shards automatically.

Persistent Storage — Manages PersistentVolumeClaims for each data node.

Health Monitoring — Watches cluster health and reports status via the Elasticsearch custom resource.

🎓 Certification Note The ECK Operator exam area focuses on understanding CRDs, the operator reconciliation loop, how node roles are configured in nodeSets, and how to manage upgrades. Know the difference between kubectl create (for CRDs) and kubectl apply (for stack resources).

Installing the ECK Operator

1

Install CRDs

# Use 'create' (not apply) for CRDs
kubectl create -f https://download.elastic.co/downloads/eck/2.14.0/crds.yaml
💡 Why create instead of apply? CRDs should use create to avoid issues during operator upgrades. This is a declarative vs imperative distinction that prevents losing control of managed resources during version transitions.
2

Install the Operator

kubectl apply -f https://download.elastic.co/downloads/eck/2.14.0/operator.yaml
3

Verify Installation

# Check operator is running
kubectl -n elastic-system get pods

# NAME                 READY   STATUS    RESTARTS   AGE
# elastic-operator-0   1/1     Running   0          30s

# Watch operator logs
kubectl -n elastic-system logs -f statefulset.apps/elastic-operator
1

Add the Elastic Helm Repo

helm repo add elastic https://helm.elastic.co
helm repo update
2

Install ECK Operator via Helm

helm install elastic-operator elastic/eck-operator \
  --namespace elastic-system \
  --create-namespace

Deploy Elasticsearch on Kubernetes

With ECK installed, deploying an Elasticsearch cluster is a single YAML file. Here's a development-friendly single-node setup and a production-grade multi-node topology.

# elasticsearch-dev.yaml
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 8.17.0
  nodeSets:
    - name: default
      count: 1
      config:
        node.store.allow_mmap: false  # needed for Docker/K3d
      podTemplate:
        spec:
          containers:
            - name: elasticsearch
              resources:
                requests:
                  memory: 2Gi
                  cpu: 500m
                limits:
                  memory: 2Gi
                  cpu: 1
# Apply it
kubectl apply -f elasticsearch-dev.yaml

# Watch it come up
kubectl get elasticsearch
# NAME         HEALTH   NODES   VERSION   PHASE   AGE
# quickstart   green    1       8.17.0    Ready   2m

# Get the password
PASSWORD=$(kubectl get secret quickstart-es-elastic-user \
  -o go-template='{{.data.elastic | base64decode}}')
echo $PASSWORD

# Test the connection
kubectl port-forward svc/quickstart-es-http 9200 &
curl -u "elastic:$PASSWORD" -k https://localhost:9200
# elasticsearch-prod.yaml
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: production
spec:
  version: 8.17.0
  nodeSets:
    # Dedicated master nodes
    - name: masters
      count: 3
      config:
        node.roles: ["master"]
      podTemplate:
        spec:
          containers:
            - name: elasticsearch
              resources:
                requests: { memory: 2Gi, cpu: 1 }
                limits:   { memory: 2Gi, cpu: 2 }
      volumeClaimTemplates:
        - metadata:
            name: elasticsearch-data
          spec:
            accessModes: ["ReadWriteOnce"]
            resources:
              requests:
                storage: 10Gi

    # Data nodes
    - name: data
      count: 3
      config:
        node.roles: ["data", "ingest"]
      podTemplate:
        spec:
          containers:
            - name: elasticsearch
              resources:
                requests: { memory: 4Gi, cpu: 2 }
                limits:   { memory: 4Gi, cpu: 4 }
      volumeClaimTemplates:
        - metadata:
            name: elasticsearch-data
          spec:
            accessModes: ["ReadWriteOnce"]
            resources:
              requests:
                storage: 100Gi

    # Coordinating nodes
    - name: coordinating
      count: 2
      config:
        node.roles: ["ingest"]
      podTemplate:
        spec:
          containers:
            - name: elasticsearch
              resources:
                requests: { memory: 2Gi, cpu: 1 }
                limits:   { memory: 2Gi, cpu: 2 }
🎓 Certification Note Understand the different node roles and why separating them matters. Master-eligible nodes should be dedicated (odd number, typically 3) to prevent split-brain scenarios. Data nodes handle the heavy lifting of indexing and search. Coordinating/ingest nodes handle request routing and pipeline processing.

Deploy Kibana

# kibana.yaml
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
spec:
  version: 8.17.0
  count: 1
  elasticsearchRef:
    name: quickstart  # Must match your Elasticsearch resource name
# Apply
kubectl apply -f kibana.yaml

# Access Kibana
kubectl port-forward svc/quickstart-kb-http 5601

# Open https://localhost:5601
# Login: elastic / (password from previous section)
💡 Kibana Dev Tools Once logged in, navigate to Management → Dev Tools. This is your interactive console for running Elasticsearch queries. You'll use this extensively for certification practice.

ECK Configuration Deep Dive

ECK exposes Elasticsearch configuration through the CRD spec. Here are the key areas you need to master.

elasticsearch.yml Overrides

Any elasticsearch.yml setting can be specified under spec.nodeSets[].config:

nodeSets:
  - name: data
    config:
      node.roles: ["data", "ingest"]
      cluster.routing.allocation.awareness.attributes: zone
      indices.memory.index_buffer_size: 20%
JVM Heap Settings

ECK automatically sets heap to 50% of the container memory limit. To override:

podTemplate:
  spec:
    containers:
      - name: elasticsearch
        env:
          - name: ES_JAVA_OPTS
            value: "-Xms4g -Xmx4g"
        resources:
          limits:
            memory: 8Gi
⚠️ Best Practice Never set heap above 50% of available memory. The OS needs the other half for the filesystem cache, which Lucene depends on heavily. And never exceed ~30GB heap to stay within the compressed OOPs threshold.
Persistent Storage
nodeSets:
  - name: data
    volumeClaimTemplates:
      - metadata:
          name: elasticsearch-data
        spec:
          accessModes: ["ReadWriteOnce"]
          storageClassName: fast-ssd   # Your StorageClass
          resources:
            requests:
              storage: 200Gi

ECK cannot resize existing PVCs. If you need more storage, you must rely on your StorageClass supporting volume expansion, or migrate data.

TLS / HTTP Certificates

ECK auto-generates self-signed certs. To use your own:

spec:
  http:
    tls:
      certificate:
        secretName: my-custom-cert   # K8s Secret with tls.crt and tls.key

To disable TLS (development only):

spec:
  http:
    tls:
      selfSignedCertificate:
        disabled: true
Update Strategy

Control how rolling upgrades proceed:

spec:
  updateStrategy:
    changeBudget:
      maxSurge: -1         # Unbounded — can create new pods first
      maxUnavailable: 1    # Only 1 pod down at a time

This ensures minimal disruption during version upgrades. The cluster health may briefly turn yellow but remains available.

ECK Operator YAML — Line-by-Line Breakdown

When you run kubectl apply -f operator.yaml, you're deploying a large all-in-one manifest that creates roughly 10 Kubernetes resources. This section breaks down every single resource, every key-value pair, and explains why each piece exists. Understanding this is essential for both the certification and real-world troubleshooting.

💡 How to Read This The all-in-one YAML is separated by --- markers. Each --- denotes a separate Kubernetes resource. They are applied in order: namespace first, then roles, then the operator itself. We'll walk through each one.

1. Namespace — elastic-system

apiVersion: v1
kind: Namespace
metadata:
  name: elastic-system
  labels:
    name: elastic-system
KeyValueExplanation
apiVersion: v1v1The API versionapiVersion tells Kubernetes which API group and version to use for this resource. v1 is the core "stable" API group. Other resources use groups like apps/v1, rbac.authorization.k8s.io/v1, etc. If you use the wrong apiVersion, Kubernetes won't recognize your YAML. for core Kubernetes objects like Namespaces, Pods, and Services. v1 means this is part of the stable core API.
kind: NamespaceNamespaceA NamespaceNamespaces are virtual clusters within a physical Kubernetes cluster. They provide isolation for resources — pods, services, and secrets in one namespace are invisible to other namespaces by default. Think of them as "folders" that organize your cluster. Common examples: default, kube-system, elastic-system. is a virtual partition of the cluster. This creates a dedicated home for all ECK operator resources, keeping them isolated from your application workloads.
metadata.nameelastic-systemThe name of the namespace. All ECK components (the operator pod, its secrets, its service account, webhook service) will live here.
metadata.labelsname: elastic-systemLabels are key-value tags for organizing and selecting resources. This label lets you query all resources in this namespace easily: kubectl get all -l name=elastic-system.
🎓 Why This Matters Keeping the operator in its own namespace is a best practice. It prevents accidental deletion of operator resources when cleaning up application namespaces. Your Elasticsearch clusters can live in a different namespace (like default or observability), and the operator will still manage them because it has cluster-wide permissions.

2. ServiceAccount — elastic-operator

apiVersion: v1
kind: ServiceAccount
automountServiceAccountToken: true
metadata:
  name: elastic-operator
  namespace: elastic-system
  labels:
    control-plane: elastic-operator
    app.kubernetes.io/version: "3.0.0"
KeyValueExplanation
kind: ServiceAccountA ServiceAccountServiceAccount is the identity a Pod uses to interact with the Kubernetes API. When the operator pod needs to create a StatefulSet, read a Secret, or update an Elasticsearch custom resource, it authenticates as this ServiceAccount. Without it, the pod would use the default service account, which typically has no permissions. provides an identity for the operator pod. It's like a "username" the operator uses when talking to the Kubernetes API server.
automountServiceAccountTokentrueAutomatically mounts the service account's API token into the pod at /var/run/secrets/kubernetes.io/serviceaccount/token. The operator needs this token to authenticate every API call it makes (creating pods, reading secrets, etc.).
metadata.namespaceelastic-systemThe ServiceAccount lives in the same namespace as the operator. ServiceAccounts are namespace-scoped resources.
labels.control-planeelastic-operatorA common labeling convention. control-plane identifies this as part of the operator's control infrastructure (not a user workload). Used by selectors throughout the manifest.
labels.app.kubernetes.io/version"3.0.0"A standard Kubernetes label from the recommended label setRecommended Labels
Kubernetes has a set of standard labels:
app.kubernetes.io/name — app name
app.kubernetes.io/version — app version
app.kubernetes.io/component — role in architecture
app.kubernetes.io/managed-by — tool managing it

These make it easy to identify and query resources uniformly across tools like Helm, kubectl, and monitoring systems.
. Tracks which ECK version deployed this resource. Helpful for auditing and upgrades.

3. Secret — elastic-webhook-server-cert

apiVersion: v1
kind: Secret
metadata:
  name: elastic-webhook-server-cert
  namespace: elastic-system
  labels:
    control-plane: elastic-operator
    app.kubernetes.io/version: "3.0.0"
KeyValueExplanation
kind: SecretA SecretKubernetes Secrets store sensitive data like passwords, TLS certificates, and API tokens. They are base64-encoded (not encrypted by default!) and can be mounted as files or environment variables in pods. In production, enable encryption at rest with EncryptionConfiguration. stores sensitive data. This one holds the TLS certificate and private key the webhook server uses for HTTPS.
nameelastic-webhook-server-certInitially created empty. The operator generates and populates this Secret at startup with a self-signed TLS cert. The webhook service needs HTTPS because the Kubernetes API server only sends webhook requests over TLS.
💡 How It Works When the operator pod starts, it generates a CA and server certificate, writes them into this Secret, and mounts them into the pod. The ValidatingWebhookConfiguration (at the bottom of the manifest) references this cert so the K8s API server can verify the webhook's identity. This is all automatic — you never need to create certs manually.

4. ConfigMap — elastic-operator (Operator Settings)

This is the operator's main configuration file. It controls how the ECK operator behaves — logging, reconciliation speed, certificate lifetimes, telemetry, and more.

apiVersion: v1
kind: ConfigMap
metadata:
  name: elastic-operator
  namespace: elastic-system
data:
  eck.yaml: |-
    log-verbosity: 0
    metrics-port: 0
    metrics-secure: false
    container-registry: docker.elastic.co
    max-concurrent-reconciles: 3
    ca-cert-validity: 8760h
    ca-cert-rotate-before: 24h
    cert-validity: 8760h
    cert-rotate-before: 24h
    disable-config-watch: false
    exposed-node-labels: [topology.kubernetes.io/.*,failure-domain.beta.kubernetes.io/.*]
    set-default-security-context: auto-detect
    kube-client-timeout: 60s
    elasticsearch-client-timeout: 180s
    disable-telemetry: false
    distribution-channel: all-in-one
    validate-storage-class: true
    enable-webhook: true
    webhook-name: elastic-webhook.k8s.elastic.co
    webhook-port: 9443
    operator-namespace: elastic-system
    enable-leader-election: true
    elasticsearch-observation-interval: 10s
    ubi-only: false
SettingValueWhat It Does & Why
log-verbosity0 Controls how chatty the operator logs are. 0 = standard info-level logging. Increase to 1 or 2 for debug output when troubleshooting. Higher values produce significantly more log lines.
metrics-port0 Port for exposing Prometheus-compatible metrics. 0 means metrics are disabled. Set to a port like 8080 if you want to scrape operator performance metrics with Prometheus.
metrics-securefalse Whether the metrics endpoint requires TLS. Only relevant if metrics-port is non-zero.
container-registrydocker.elastic.co The container registryContainer Registry
A registry is where container images are stored and distributed. docker.elastic.co is Elastic's official registry. In air-gapped or enterprise environments, you'd change this to your private registry (e.g., my-registry.company.com/elastic) and mirror the images there.
where the operator pulls Elastic Stack images (Elasticsearch, Kibana, Beats, etc.). Change this if you use a private/mirrored registry.
max-concurrent-reconciles3 How many reconciliation loopsReconciliation Loop
The core of the Operator Pattern. The operator continuously compares the desired state (your YAML) with the actual state (what's running). If they differ, it takes action to bring reality in line with your spec. Each reconcile handles one Elasticsearch resource. With max-concurrent-reconciles: 3, three different ES clusters can be reconciled simultaneously.
can run at the same time. If you manage dozens of Elasticsearch clusters, increase this to speed up processing. Too high a value may overwhelm the K8s API server.
ca-cert-validity8760h How long the operator's internal CA certificate is valid. 8760h = 1 year. The operator uses this CA to sign TLS certificates for Elasticsearch nodes and the webhook.
ca-cert-rotate-before24h How long before expiry the CA cert gets rotated. The operator will generate a new CA 24 hours before the old one expires — ensuring no gap in TLS coverage.
cert-validity8760h How long individual node/transport TLS certificates are valid (1 year). These are the certs used by each Elasticsearch pod for HTTP and inter-node encryption.
cert-rotate-before24h Rotate individual certs 24 hours before expiry. The operator handles this seamlessly — pods get new certs without restart through Kubernetes Secret updates.
disable-config-watchfalse When false, the operator watches this ConfigMap for changes and reloads configuration without a restart. Set to true only if you want to require a pod restart for config changes.
exposed-node-labels[topology.kubernetes.io/.*,
failure-domain...]
Which Kubernetes node labelsExposed Node Labels
Elasticsearch can use Kubernetes node labels for shard allocation awareness — for example, routing primary and replica shards to different availability zones. This setting controls which K8s node labels are exposed to Elasticsearch's node.attr.* settings. The regex patterns match zone/region topology labels.
are passed through to Elasticsearch pods as node attributes. Critical for zone-aware shard allocation in multi-AZ clusters.
set-default-security-contextauto-detect Whether to set a SecurityContextSecurity Context
A SecurityContext defines privilege and access settings for a pod or container: running as non-root, dropping Linux capabilities, read-only filesystems, etc. auto-detect means ECK checks if the cluster needs one (e.g., OpenShift has strict SCCs) and sets it appropriately.
on managed pods. auto-detect = ECK checks if the platform requires it (like OpenShift's Security Context Constraints) and applies it automatically.
kube-client-timeout60s Timeout for the operator's HTTP calls to the Kubernetes API server. If your K8s API is slow (large clusters, heavy load), you might need to increase this.
elasticsearch-client-timeout180s Timeout for the operator's HTTP calls to Elasticsearch clusters it manages. Set to 3 minutes because some ES operations (like shard allocation changes) can take time.
disable-telemetryfalse When false, ECK sends anonymous usage statistics to Elastic (cluster count, version info). No personal data. Set to true for air-gapped or privacy-conscious environments.
distribution-channelall-in-one Identifies how the operator was installed. all-in-one = via the single YAML manifest. Alternatives: helm, community-operators (OLM). Used in telemetry only.
validate-storage-classtrue When true, the operator checks that the StorageClass referenced in your Elasticsearch PVC templates actually exists in the cluster before deploying. Prevents cryptic failures.
enable-webhooktrue Enables the validating admission webhookValidating Webhook
A webhook that intercepts CREATE and UPDATE requests to the K8s API. Before an Elasticsearch resource is saved, the webhook validates the YAML spec: checks for invalid node roles, incompatible settings, misconfigured volume claims, etc. If validation fails, the request is rejected with an error before anything is applied. This prevents misconfigurations from reaching the operator.
. The webhook catches invalid configurations (bad node roles, incorrect settings) before they're applied to the cluster. Strongly recommended.
webhook-nameelastic-webhook.k8s.elastic.co The name of the ValidatingWebhookConfiguration resource. Must match the name in the webhook configuration at the bottom of the manifest.
webhook-port9443 The port the webhook HTTPS server listens on inside the operator pod. The webhook Service routes port 443 → this port (9443).
operator-namespaceelastic-system Tells the operator which namespace it's running in. Used to find its own resources (secrets, configmaps).
enable-leader-electiontrue Enables leader electionLeader Election
When you run multiple operator replicas for high availability, only one should actively reconcile resources at a time (the "leader"). The others remain on standby. Leader election uses a Kubernetes Lease resource — whichever replica acquires the lease becomes the active leader. If it crashes, another replica takes over within seconds.
. Critical if running multiple operator replicas for HA. Uses a Kubernetes Lease object so only one operator instance is active at a time.
elasticsearch-observation-interval10s How often the operator polls each Elasticsearch cluster's health status. Every 10 seconds it checks _cluster/health to update the .status field of the Elasticsearch custom resource.
ubi-onlyfalse If true, the operator only uses UBI-based imagesUBI (Universal Base Image)
Red Hat's UBI is a freely redistributable container base image. Some organizations (especially government/military) require all containers to be based on UBI for compliance reasons. When ubi-only: true, ECK pulls Elastic Stack images tagged with -ubi (e.g., elasticsearch:8.17.0-ubi). These are identical functionally but built on a RHEL base.
from the container registry. Required for OpenShift environments with strict compliance requirements.

5. ClusterRole — elastic-operator (Permissions)

This is the most important resource to understand. It defines exactly what the operator is allowed to do in your cluster. Every permission is here for a specific reason.

💡 RBAC Primer Kubernetes uses Role-Based Access Control (RBAC)RBAC in Kubernetes
RBAC has three pieces:
1. Subject — who (ServiceAccount, User, Group)
2. Role/ClusterRole — what they can do (rules)
3. RoleBinding/ClusterRoleBinding — connects who to what

A Role is namespace-scoped. A ClusterRole is cluster-wide. Since the operator manages resources across all namespaces, it needs a ClusterRole.
. A ClusterRole defines permissions at the cluster level (all namespaces). Each rule says: "for these API groups, for these resource types, allow these actions."
Core Kubernetes Resources
- apiGroups: [""]
  resources: [pods, events, persistentvolumeclaims,
              secrets, services, configmaps]
  verbs: [get, list, watch, create, update, patch, delete]

- apiGroups: ["apps"]
  resources: [deployments, statefulsets, daemonsets]
  verbs: [get, list, watch, create, update, patch, delete]

- apiGroups: ["policy"]
  resources: [poddisruptionbudgets]
  verbs: [get, list, watch, create, update, patch, delete]
ResourceWhy the Operator Needs It
podsCreates, monitors, and deletes Elasticsearch, Kibana, and other Elastic pods. Reads pod status to track health.
eventsCreates Kubernetes events to report what it's doing (e.g., "Starting rolling upgrade", "Scaling data nodes").
persistentvolumeclaimsManages storage for Elasticsearch data nodes. Each data node gets its own PVC for index storage.
secretsStores and manages TLS certificates, the elastic user password, internal transport keys, and webhook certs.
servicesCreates Kubernetes Services to expose Elasticsearch (port 9200) and inter-node transport (port 9300).
configmapsManages Elasticsearch configuration (elasticsearch.yml) and Kibana settings for each deployment.
statefulsetsThe primary workload type for Elasticsearch. StatefulSets provide stable pod names, ordered deployment, and persistent storage.
deploymentsUsed for Kibana, APM Server, and Enterprise Search — stateless components that don't need StatefulSet semantics.
daemonsetsUsed for Beats and Elastic Agent — components that need to run on every node for log/metric collection.
poddisruptionbudgetsPDBsPodDisruptionBudgets (PDBs)
A PDB limits how many pods of a set can be down simultaneously during voluntary disruptions (node drains, upgrades). The operator creates PDBs for Elasticsearch to prevent Kubernetes from evicting too many data nodes at once, which could cause data loss or a red cluster.
protect ES pods during node maintenance. Prevents K8s from draining too many ES nodes simultaneously.
Leader Election & Auth
- apiGroups: ["authorization.k8s.io"]
  resources: [subjectaccessreviews]
  verbs: [create]

- apiGroups: ["coordination.k8s.io"]
  resources: [leases]
  verbs: [create]

- apiGroups: ["coordination.k8s.io"]
  resources: [leases]
  resourceNames: [elastic-operator-leader]
  verbs: [get, watch, update]
PermissionWhy
subjectaccessreviewsLets the operator check if it has permission to perform an action. Used for self-authorization checks before attempting operations.
leases (create)Creates the leader election Lease object. Only the first replica to acquire this lease becomes the active operator.
leases (get/watch/update)Specifically for the lease named elastic-operator-leader. The active leader renews this lease periodically. If it fails to renew (crash), another replica takes over.
Elastic Custom Resources (CRDs)

The operator needs permissions for every Elastic CRD it manages. Each follows the same pattern:

- apiGroups: ["elasticsearch.k8s.elastic.co"]
  resources: [elasticsearches, elasticsearches/status, elasticsearches/finalizers]
  verbs: [get, list, watch, create, update, patch]
Sub-ResourcePurpose
elasticsearchesThe main custom resource — your Elasticsearch cluster spec. The operator reads your desired state from this.
elasticsearches/statusThe status subresource where the operator writes health, phase (Ready/ApplyingChanges), version, and node count.
elasticsearches/finalizersFinalizersKubernetes Finalizers
Finalizers are hooks that run before a resource is deleted. When you delete an Elasticsearch CR, the operator's finalizer ensures it cleanly removes dependent resources (StatefulSets, Services, Secrets) before the CR disappears. Without finalizers, you could end up with orphaned resources. The blockOwnerDeletion note in the YAML refers to OpenShift-specific behavior with owner references.
ensure clean deletion. When you kubectl delete elasticsearch quickstart, the operator cleans up all child resources before the CR is removed.

The same pattern repeats for every Elastic component the operator manages:

API GroupResourceComponent
elasticsearch.k8s.elastic.coelasticsearchesElasticsearch clusters
kibana.k8s.elastic.cokibanasKibana instances
apm.k8s.elastic.coapmserversAPM Server
beat.k8s.elastic.cobeatsFilebeat, Metricbeat, etc.
agent.k8s.elastic.coagentsElastic Agent
enterprisesearch.k8s.elastic.coenterprisesearchesEnterprise Search
maps.k8s.elastic.coelasticmapsserversElastic Maps Server
logstash.k8s.elastic.cologstashesLogstash
stackconfigpolicy.k8s.elastic.costackconfigpoliciesStack-wide config policies
autoscaling.k8s.elastic.coelasticsearchautoscalersES autoscaling rules
Infrastructure Permissions
- apiGroups: ["storage.k8s.io"]
  resources: [storageclasses]
  verbs: [get, list, watch]

- apiGroups: ["admissionregistration.k8s.io"]
  resources: [validatingwebhookconfigurations]
  verbs: [get, list, watch, create, update, patch, delete]

- apiGroups: [""]
  resources: [nodes, endpoints]
  verbs: [get, list, watch]
ResourceWhy
storageclasses (read-only)Validates that StorageClasses referenced in your PVC templates exist before deploying (the validate-storage-class: true setting).
validatingwebhookconfigurationsManages the webhook configuration itself — creates it, updates its CA bundle when certs rotate, and cleans up on uninstall.
nodes (read-only)Reads node labels (like availability zone) to pass to Elasticsearch for zone-aware shard allocation.
endpoints (read-only)Watches Service endpoints to track which ES pods are ready and discoverable.

6. Aggregate ClusterRoles — elastic-operator-view & elastic-operator-edit

These are not for the operator itself — they are for your team. They use Kubernetes role aggregationRole Aggregation
Kubernetes has built-in ClusterRoles: view, edit, and admin. When you add the label rbac.authorization.k8s.io/aggregate-to-view: "true" to a ClusterRole, its rules are automatically merged into the built-in view role. This means anyone with the view role can now also view Elasticsearch resources — no extra RoleBindings needed.
so team members can work with Elastic resources using standard K8s roles.

ClusterRoleAggregates IntoPermissions
elastic-operator-viewview, edit, adminget, list, watch on all Elastic CRDs — team members with "view" access can see your Elasticsearch clusters, Kibana instances, etc.
elastic-operator-editedit, admincreate, delete, patch, update on all Elastic CRDs — team members with "edit" access can deploy and modify Elastic resources.

7. ClusterRoleBinding — Connects Identity to Permissions

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: elastic-operator
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: elastic-operator        # The ClusterRole defined above
subjects:
- kind: ServiceAccount
  name: elastic-operator         # The ServiceAccount defined above
  namespace: elastic-system

This is the glue that connects the ServiceAccount (identity, resource #2) to the ClusterRole (permissions, resource #5). Without this binding, the operator pod would have zero permissions and fail immediately on startup.

8. Service — elastic-webhook-server

apiVersion: v1
kind: Service
metadata:
  name: elastic-webhook-server
  namespace: elastic-system
spec:
  ports:
  - name: https
    port: 443
    targetPort: 9443
  selector:
    control-plane: elastic-operator
KeyValueExplanation
port: 443The port the K8s API server connects to when sending webhook requests. HTTPS on the standard port.
targetPort: 9443Routes to port 9443 inside the operator pod, where the webhook HTTPS server is actually listening (matching webhook-port in the ConfigMap).
selectorcontrol-plane: elastic-operatorSelects pods with this label — routes traffic to the operator pod. This label was set on the StatefulSet's pod template.

9. StatefulSet — The Operator Pod Itself

This is the heart of the deployment — the actual container that runs the ECK operator code.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: elastic-operator
  namespace: elastic-system
spec:
  selector:
    matchLabels:
      control-plane: elastic-operator
  serviceName: elastic-operator
  replicas: 1
KeyValueExplanation
kind: StatefulSetA StatefulSetWhy StatefulSet for the operator?
The operator uses a StatefulSet (not a Deployment) because it provides a stable pod name (elastic-operator-0). This matters for leader election and for the webhook certificate, which includes the pod's stable DNS name. A Deployment would give random pod names that change on restart.
ensures the operator pod has a stable identity. This matters for leader election and consistent webhook addressing.
replicas: 1Runs a single operator instance. For HA, increase to 2 or 3 — leader election ensures only one is active.
serviceNameelastic-operatorProvides a headless service for the StatefulSet (stable DNS for pods).

Pod Template — Annotations

template:
  metadata:
    annotations:
      "co.elastic.logs/raw": "[{\"type\":\"container\",...}]"
      "checksum/config": 3fdaff4a979089797cb469...
AnnotationPurpose
co.elastic.logs/rawTells Filebeat/Elastic Agent (if deployed) how to parse the operator's logs. It renames fields like errorerror.message and sourceevent.source to avoid conflicts with the Elastic Common Schema (ECS).
checksum/configA SHA256 hash of the ConfigMap content. When the ConfigMap changes, this checksum changes, which triggers a pod restart. This is a common Kubernetes pattern to ensure pods pick up config changes.

Pod Template — Security

spec:
  terminationGracePeriodSeconds: 10
  serviceAccountName: elastic-operator
  automountServiceAccountToken: true
  securityContext:
    runAsNonRoot: true
  containers:
  - name: manager
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop: [ALL]
      readOnlyRootFilesystem: true
      runAsNonRoot: true
SettingWhat & Why
terminationGracePeriodSeconds: 10Gives the operator 10 seconds to shut down cleanly when deleted. The operator saves its state and releases the leader lease during this window.
serviceAccountNameLinks this pod to the ServiceAccount with all the RBAC permissions defined earlier.
runAsNonRoot: trueThe pod and container must run as a non-root user. Security hardening — even if the container image has a root user, K8s will refuse to start it.
allowPrivilegeEscalation: falsePrevents the process from gaining more privileges than its parent. Blocks exploit techniques that try to escalate to root.
capabilities.drop: [ALL]Drops all Linux capabilities (like NET_RAW, SYS_ADMIN). The operator doesn't need any special kernel privileges. This is the most restrictive setting possible.
readOnlyRootFilesystem: trueThe container filesystem is mounted read-only. Prevents malicious code from writing to the container. The operator writes only to /tmp (mounted separately for certs).

Container — Image, Args, Env, Resources

containers:
- image: "docker.elastic.co/eck/eck-operator:3.0.0"
  imagePullPolicy: IfNotPresent
  name: manager
  args: ["manager", "--config=/conf/eck.yaml"]
  env:
  - name: OPERATOR_NAMESPACE
    valueFrom:
      fieldRef:
        fieldPath: metadata.namespace
  - name: POD_IP
    valueFrom:
      fieldRef:
        fieldPath: status.podIP
  - name: WEBHOOK_SECRET
    value: elastic-webhook-server-cert
  resources:
    limits:   { cpu: 1, memory: 1Gi }
    requests: { cpu: 100m, memory: 150Mi }
SettingExplanation
imageThe ECK operator container image, version 3.0.0, from Elastic's official registry.
imagePullPolicy: IfNotPresentOnly pulls the image if it's not already cached on the node. Speeds up restarts and avoids unnecessary network calls.
args: ["manager", "--config=..."]Starts the operator binary in "manager" mode (as opposed to "webhook-only" mode) and points it to the ConfigMap-mounted config file.
OPERATOR_NAMESPACEInjected from the Downward APIKubernetes Downward API
The Downward API exposes pod and container metadata to the running container as environment variables or files. fieldRef: metadata.namespace injects the pod's namespace. fieldRef: status.podIP injects the pod's IP. The pod doesn't need to "know" these values in advance — K8s fills them in automatically at runtime.
. Tells the operator which namespace it's in, so it can find its own resources.
POD_IPThe operator's own IP address. Used for the webhook server to bind to the correct network interface.
WEBHOOK_SECRETName of the Secret where the webhook TLS cert is stored. The operator reads and writes certs to this Secret.
resources.requests100m CPU (0.1 core) and 150Mi memory — the minimum guaranteed. The scheduler uses requests to place the pod.
resources.limits1 CPU and 1Gi memory — the maximum. If the operator exceeds the memory limit, K8s kills the pod (OOMKilled). CPU is throttled, not killed.

Volume Mounts

volumeMounts:
- mountPath: "/conf"
  name: conf
  readOnly: true
- mountPath: /tmp/k8s-webhook-server/serving-certs
  name: cert
  readOnly: true
volumes:
- name: conf
  configMap:
    name: elastic-operator
- name: cert
  secret:
    defaultMode: 420
    secretName: elastic-webhook-server-cert
VolumeSourceWhat Gets Mounted
conf/confConfigMap: elastic-operatorThe eck.yaml config file. Mounted read-only. The operator reads its settings from here on startup (and watches for changes).
cert/tmp/k8s-webhook-server/serving-certsSecret: elastic-webhook-server-certThe TLS cert and key for the webhook HTTPS server. defaultMode: 420 (octal 0644) means the cert files are readable by the container process.

10. ValidatingWebhookConfiguration — Request Interceptor

This resource tells the Kubernetes API server: "Before accepting any CREATE or UPDATE to an Elastic custom resource, send it to the webhook service for validation first."

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: elastic-webhook.k8s.elastic.co
webhooks:
- clientConfig:
    service:
      name: elastic-webhook-server
      namespace: elastic-system
      path: /validate-elasticsearch-k8s-elastic-co-v1-elasticsearch
  failurePolicy: Ignore
  name: elastic-es-validation-v1.k8s.elastic.co
  matchPolicy: Exact
  admissionReviewVersions: [v1, v1beta1]
  sideEffects: None
  rules:
  - apiGroups: ["elasticsearch.k8s.elastic.co"]
    apiVersions: ["v1"]
    operations: ["CREATE", "UPDATE"]
    resources: ["elasticsearches"]
KeyWhat It Does
clientConfig.servicePoints to the webhook Service in elastic-system. The K8s API server sends HTTPS requests to this service when someone creates/updates an Elastic resource.
pathEach Elastic resource type has its own validation endpoint (path). For example, /validate-elasticsearch-k8s-elastic-co-v1-elasticsearch handles Elasticsearch v1 resources, /validate-kibana-k8s-elastic-co-v1-kibana handles Kibana, etc.
failurePolicy: IgnoreIf the webhook is unreachable (operator pod is down, restarting), Kubernetes allows the request through anyway. The alternative is Fail, which would block all Elastic resource changes if the operator is down. Ignore is safer for availability.
matchPolicy: ExactOnly intercepts requests that exactly match the specified API group and version. No wildcards.
admissionReviewVersionsSupports both v1 and v1beta1 admission review formats for backward compatibility with older K8s clusters.
sideEffects: NoneDeclares that this webhook has no side effects — it only validates, never modifies resources or external state. This is important for K8s to know it can safely retry webhook calls.
rulesMatch conditions: intercept only CREATE and UPDATE operations on the specified resource types. DELETE and GET are not intercepted.
💡 The Full Webhook List The manifest registers 14 webhooks — one for each Elastic CRD and API version. They all follow the identical pattern above. The resources covered are: Agent (v1alpha1), APM Server (v1, v1beta1), Beat (v1beta1), Enterprise Search (v1, v1beta1), Elasticsearch (v1, v1beta1), Maps Server (v1alpha1), Kibana (v1, v1beta1), ES Autoscaler (v1alpha1), StackConfigPolicy (v1alpha1), and Logstash (v1alpha1).

Summary — How All 10 Resources Work Together

📂
Namespace
elastic-system — the operator's home
🪪
ServiceAccount
Operator's identity
🔐
ClusterRole + Binding
Permissions to manage everything
⚙️
ConfigMap
Operator settings (eck.yaml)
🏃
StatefulSet
Runs the operator pod
🛡️
Webhook
Validates your YAML before apply

The flow is: you write Elasticsearch YAML → the webhook validates it → Kubernetes saves it → the operator pod (running as the ServiceAccount with ClusterRole permissions) detects the change → reads the ConfigMap for its settings → creates StatefulSets, Services, Secrets, and PDBs to make your cluster real.

🎓 Certification Note For the exam, you need to understand: what each component of the operator deployment does, how CRDs extend the K8s API, the role of the webhook in validation, RBAC roles and bindings, and the difference between kubectl create (for CRDs) vs kubectl apply (for the operator). You should also be able to troubleshoot operator issues by checking its logs: kubectl -n elastic-system logs -f statefulset.apps/elastic-operator.

Cluster Administration

Essential Elasticsearch APIs for managing your cluster. Practice these in Kibana Dev Tools.

Health & Status

# Cluster health (green/yellow/red)
GET _cluster/health

# Node info
GET _cat/nodes?v

# Shard allocation
GET _cat/shards?v

# Indices overview
GET _cat/indices?v&s=index

# Pending tasks
GET _cluster/pending_tasks

Index Management

# Create an index with settings and mappings
PUT /my-index
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "title":     { "type": "text" },
      "status":    { "type": "keyword" },
      "timestamp": { "type": "date" },
      "count":     { "type": "integer" }
    }
  }
}

# Create an index template
PUT _index_template/logs-template
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 2,
      "number_of_replicas": 1
    },
    "mappings": {
      "properties": {
        "message":   { "type": "text" },
        "@timestamp": { "type": "date" },
        "level":     { "type": "keyword" }
      }
    }
  }
}

# Index Lifecycle Management (ILM) policy
PUT _ilm/policy/logs-policy
{
  "policy": {
    "phases": {
      "hot":    { "actions": { "rollover": { "max_size": "50GB", "max_age": "1d" }}},
      "warm":   { "min_age": "7d",  "actions": { "shrink": { "number_of_shards": 1 }}},
      "cold":   { "min_age": "30d", "actions": { "freeze": {} }},
      "delete": { "min_age": "90d", "actions": { "delete": {} }}
    }
  }
}
🎓 Certification Note ILM policies, index templates, component templates, and data streams are heavily tested. Make sure you can create them from memory using only the official docs as reference. Practice creating a complete data stream setup: component template → index template → ILM policy → data stream.

Setting Up Data Ingestion

Getting data into Elasticsearch is fundamental. Here are the main approaches, from simplest to most powerful.

Direct API (Single Documents)
# Index a single document
POST /my-index/_doc
{
  "title": "Getting Started with ES",
  "status": "published",
  "timestamp": "2026-04-01T12:00:00Z",
  "count": 42
}

# Index with a specific ID
PUT /my-index/_doc/1
{
  "title": "ECK Tutorial",
  "status": "draft",
  "timestamp": "2026-04-01T14:00:00Z",
  "count": 7
}
Bulk API (Batch Ingestion)
# Bulk index multiple documents
POST _bulk
{ "index": { "_index": "my-index" } }
{ "title": "Doc 1", "status": "active", "timestamp": "2026-04-01T10:00:00Z" }
{ "index": { "_index": "my-index" } }
{ "title": "Doc 2", "status": "active", "timestamp": "2026-04-01T11:00:00Z" }
{ "index": { "_index": "my-index" } }
{ "title": "Doc 3", "status": "archived", "timestamp": "2026-03-01T09:00:00Z" }
💡 Bulk Best Practices Send 5–15 MB per bulk request. Use the _bulk endpoint rather than individual index calls whenever you have more than a handful of documents. Each line must be on its own line (NDJSON format).
Ingest Pipelines

Process documents before indexing with built-in processors:

# Create an ingest pipeline
PUT _ingest/pipeline/my-pipeline
{
  "description": "Extract timestamp and add geo data",
  "processors": [
    {
      "date": {
        "field": "raw_timestamp",
        "formats": ["yyyy-MM-dd HH:mm:ss"],
        "target_field": "@timestamp"
      }
    },
    {
      "geoip": {
        "field": "client_ip",
        "target_field": "geo"
      }
    },
    {
      "remove": {
        "field": "raw_timestamp"
      }
    }
  ]
}

# Use the pipeline when indexing
POST /my-index/_doc?pipeline=my-pipeline
{
  "raw_timestamp": "2026-04-01 12:30:00",
  "client_ip": "8.8.8.8",
  "message": "User login"
}
Beats & Elastic Agent (On Kubernetes)

Deploy Filebeat as a DaemonSet to collect container logs from every node:

# filebeat.yaml (ECK managed)
apiVersion: beat.k8s.elastic.co/v1beta1
kind: Beat
metadata:
  name: filebeat
spec:
  type: filebeat
  version: 8.17.0
  elasticsearchRef:
    name: quickstart
  daemonSet:
    podTemplate:
      spec:
        containers:
          - name: filebeat
            volumeMounts:
              - name: varlog
                mountPath: /var/log
                readOnly: true
        volumes:
          - name: varlog
            hostPath:
              path: /var/log
  config:
    filebeat.inputs:
      - type: container
        paths:
          - /var/log/containers/*.log
Logstash

For complex ETL pipelines with multiple inputs/outputs:

# logstash.conf
input {
  beats { port => 5044 }
  tcp   { port => 5000 codec => json }
}

filter {
  if [type] == "syslog" {
    grok {
      match => { "message" => "%{SYSLOGTIMESTAMP:timestamp} %{SYSLOGHOST:host} %{DATA:program}: %{GREEDYDATA:msg}" }
    }
  }
  mutate {
    remove_field => ["agent", "ecs"]
  }
}

output {
  elasticsearch {
    hosts => ["https://quickstart-es-http:9200"]
    index => "logs-%{+YYYY.MM.dd}"
    user => "elastic"
    password => "${ES_PASSWORD}"
    ssl_certificate_verification => false
  }
}

Monitoring & Logging

Essential Monitoring APIs

# Cluster health
GET _cluster/health

# Node stats (CPU, memory, disk, JVM)
GET _nodes/stats

# Index stats
GET /my-index/_stats

# Hot threads (find slow queries)
GET _nodes/hot_threads

# Task management (long-running operations)
GET _tasks?actions=*search&detailed

# Shard allocation explanation
GET _cluster/allocation/explain

Kibana Stack Monitoring

Enable monitoring in your ECK Elasticsearch resource to see cluster dashboards in Kibana:

spec:
  monitoring:
    metrics:
      elasticsearchRefs:
        - name: quickstart   # Send metrics to same cluster (or a dedicated monitoring cluster)
    logs:
      elasticsearchRefs:
        - name: quickstart

Security Configuration

ECK Security (Enabled by Default)

ECK automatically enables security with TLS on both HTTP and transport layers. Key things it manages:

# ECK creates these automatically:
# - Self-signed CA certificate
# - Node transport certificates
# - HTTP certificates
# - elastic superuser with random password

# Retrieve the elastic user password:
kubectl get secret quickstart-es-elastic-user \
  -o go-template='{{.data.elastic | base64decode}}'

# Create additional users via the API:
POST _security/user/my-app-user
{
  "password": "secure-password-here",
  "roles": ["reader"],
  "full_name": "App Service Account"
}

# Create a role
POST _security/role/reader
{
  "indices": [
    {
      "names": ["logs-*"],
      "privileges": ["read", "view_index_metadata"]
    }
  ]
}
🎓 Certification Note Security topics on the exam include: creating users and roles, configuring role-based access control, understanding TLS/SSL configuration, and setting up cross-cluster search with security. Practice creating roles with field-level and document-level security.

Elastic Certified Engineer — Exam Topics

The exam is hands-on and performance-based. You complete real-world tasks on live Elasticsearch clusters in a proctored environment. The only resource allowed during the exam is the official Elastic documentation.

Exam Domain Breakdown

DomainKey Skills
Data Management Define indices, index templates, dynamic templates, ILM policies, data streams, component templates
Searching Data Term/phrase queries, boolean queries, async search, aggregations (metric, bucket, pipeline, sub-aggs), cross-cluster search, runtime fields
Developing Search Apps Highlighting, sorting, pagination, search templates
Data Processing Ingest pipelines, processors (grok, date, geoip, script, etc.), enrich policies, reindex API, update by query
Cluster Management Node roles, shard allocation, cluster settings, snapshots and restore, cross-cluster replication, diagnostics
⚠️ Important Exam Tips The exam is 3 hours with approximately 10 tasks on 2 clusters. Get comfortable navigating the Elastic documentation quickly — it's your only lifeline. Practice typing queries from scratch without auto-complete. Know where to find specific API docs within the documentation structure.

Study Checklist

Track your progress through each exam topic. Click items to mark them complete. Your progress is saved in this session.

Data Management

  • Create an index with custom settings, mappings, and aliases
  • Define and use index templates with patterns
  • Create and use component templates
  • Define dynamic templates for automatic field mapping
  • Set up an ILM policy (hot → warm → cold → delete)
  • Create a data stream with matching index template
  • Use the Reindex API to migrate data between indices
  • Use Update By Query to modify documents in place
  • Configure nested object relationships in mappings

Searching Data

Search Applications & Data Processing

  • Implement search highlighting
  • Sort results by field, score, geo_distance
  • Implement pagination (from/size and search_after)
  • Create and test ingest pipelines
  • Use grok, date, geoip, set, rename, and script processors
  • Set up an enrich policy and processor

Cluster Management & ECK

  • Diagnose and fix yellow/red cluster health
  • Configure shard allocation awareness and filtering
  • Set up snapshot repository and take/restore snapshots
  • Configure cross-cluster replication (CCR)
  • Deploy Elasticsearch on Kubernetes with ECK
  • Manage ECK upgrades and scaling operations
  • Configure node roles and resource allocation
  • Set up security: users, roles, TLS

Official Documentation & Links

💡 Final Advice The single best thing you can do to prepare for the certification is to practice with a live cluster. Set up the Docker Compose or K3d environment from this tutorial and work through every exam topic using only the official documentation. Build muscle memory for navigating the docs — that skill alone can make or break your exam experience.