How To

Install Wazuh Server on Ubuntu 24.04 LTS Step by Step

Before you pick a Wazuh installation method, decide whether this server will ever be more than one box. The installation assistant is the quickest route to a working SIEM, but it hides every decision it makes, and the first time you need to move the indexer to its own node or replace a certificate you end up reverse engineering a script you never read.

Original content from computingforgeeks.com - post 90177

This guide is the Wazuh server installation on Ubuntu 24.04 LTS done step by step, the way the Wazuh documentation calls the manual method: we generate the certificates ourselves, then install and configure the Wazuh indexer, the Wazuh manager with Filebeat, and the Wazuh dashboard one package at a time. After that we lock the setup down with rotated passwords, a correctly sized and memory-locked indexer heap, password-protected agent enrollment, and file integrity monitoring pushed from the manager. We ran all of it on Ubuntu 24.04.4 LTS with Wazuh 4.14.8, so the outputs, timings and errors below are the ones we actually hit, and when we rechecked the Wazuh repository in November 2026 it was serving 4.14.9, which the version detection in the variables step picks up on its own. The one exception is public certificate issuance: our lab has no public IP, so we ran the certificate deploy hook against a locally signed certificate rather than a live Let’s Encrypt order. If you want the one-command route instead, the Wazuh all-in-one install on Ubuntu 26.04 uses the assistant and covers its quirks.

What a step-by-step Wazuh server installation gives you

Every component in this build gets its own certificate and its own node name, so the single server you end up with is already shaped like a cluster of one. That is the real reason to do it by hand, and it is worth the extra time only if growth is on the roadmap.

Wazuh step-by-step architecture diagram on Ubuntu 24.04: dashboard, manager, Filebeat and indexer on siem-eu-west-1a 192.168.1.138, agent app-eu-west-1a on 1514 and 1515

Agents reach the manager on 1514 for events and 1515 for enrollment, and the browser only ever touches the dashboard on 443. The detail that matters for later is that the indexer binds the LAN address of the server instead of loopback, and Filebeat and the manager reach it over TLS with their own certificate plus keystore credentials, so moving the indexer to another machine means editing a few hostnames rather than reinstalling. What you pay for that flexibility is about forty extra commands compared with the assistant, and every one of them is shown below with the output we got.

Size the server before you start

Wazuh load comes from two numbers: how many events per second your agents send, which drives manager CPU and indexer heap, and how many days of alerts you keep searchable, which drives disk. The Wazuh quickstart puts a single node serving up to 25 agents at 4 vCPU, 8 GB of RAM and 50 GB of disk for 90 days of alerts, and 51 to 100 agents at 8 vCPU, 8 GB and 200 GB. In practice we’d give a single node 16 GB as soon as it serves more than a few dozen chatty servers, because the indexer heap should be half the RAM and the manager, Filebeat and dashboard still need room next to it.

Our lab server was a 4 vCPU, 8 GB VM with a 60 GB disk named siem-eu-west-1a at 192.168.1.138, plus a 2 GB Ubuntu 24.04 VM called app-eu-west-1a at 192.168.1.171 as the agent. Treat that as a floor for following along, not a production recommendation. You also need a DNS name with an A record for the dashboard if you want a trusted certificate (any DNS provider works), and nothing else bound to port 443 on the server.

Work from a root shell and set the variables

Nearly every command in the Wazuh manual install runs as root and several of them pipe into tar or tee, so we do the server side from a root shell instead of prefixing everything with sudo:

sudo -i

Three values repeat through the whole build, so we set them once. HOST_IP is the address the components use to find each other, so use the LAN IP of the server from hostname -I, not 127.0.0.1. The version is detected from GitHub, filtered to the newest stable 4.x tag so release candidates never slip in, and WAZUH_MINOR is the matching branch on the Wazuh package server:

export HOST_IP="192.168.1.138"
export WAZUH_VER=$(curl -s "https://api.github.com/repos/wazuh/wazuh/releases?per_page=30" | grep -oE '"tag_name": "v4\.[0-9]+\.[0-9]+"' | grep -oE '4\.[0-9]+\.[0-9]+' | sort -V | tail -1)
export WAZUH_MINOR="${WAZUH_VER%.*}"
echo "IP: ${HOST_IP}  Wazuh: ${WAZUH_VER}  docs branch: ${WAZUH_MINOR}"

All three should be filled in before you continue:

IP: 192.168.1.138  Wazuh: 4.14.8  docs branch: 4.14

Exports do not survive a new SSH session, so if you reconnect halfway through, run sudo -i and this block again before picking up where you left off, along with ADMIN_PASS and SITE_DOMAIN once you reach the steps that set them. Refresh the package lists and install the packages the three components depend on while you’re here:

apt-get update && apt-get install -y debconf adduser procps curl gnupg apt-transport-https debhelper tar libcap2-bin

With the dependencies in place, the server is ready for the certificates every Wazuh component needs.

Generate the certificates for every node

Wazuh components only talk to each other over TLS, and the certificates tool builds a private CA plus one certificate per node from a small config.yml. Download both into the home directory of root, which is where every later command expects them to be:

cd /root
curl -sO "https://packages.wazuh.com/${WAZUH_MINOR}/wazuh-certs-tool.sh"
curl -sO "https://packages.wazuh.com/${WAZUH_MINOR}/config.yml"

The template names three nodes (node-1 for the indexer, wazuh-1 for the manager, dashboard for the web UI) and leaves their IPs as placeholders. On a single server all three get the same address, so one sed fills them in:

sed -i -e "s/<indexer-node-ip>/${HOST_IP}/g" -e "s/<wazuh-manager-ip>/${HOST_IP}/g" -e "s/<dashboard-node-ip>/${HOST_IP}/g" config.yml
grep -E 'name:|ip:' config.yml | grep -v '#'

Each active node now carries the server IP. The commented lines for node-2 and wazuh-2 are where a cluster would grow later:

    - name: node-1
      ip: "192.168.1.138"
    - name: wazuh-1
      ip: "192.168.1.138"
    - name: dashboard
      ip: "192.168.1.138"

Generate everything with -A, then pack it into one archive and delete the loose files:

bash ./wazuh-certs-tool.sh -A
tar -cvf ./wazuh-certificates.tar -C ./wazuh-certificates/ .
rm -rf ./wazuh-certificates

The tool finishes in about two seconds and prints one line per certificate it creates:

07/10/2026 23:01:54 INFO: Verbose logging redirected to /root/wazuh-certificates-tool.log
07/10/2026 23:01:54 INFO: Generating the root certificate.
07/10/2026 23:01:54 INFO: Generating Admin certificates.
07/10/2026 23:01:54 INFO: Admin certificates created.
07/10/2026 23:01:55 INFO: Generating Wazuh indexer certificates.
07/10/2026 23:01:55 INFO: Wazuh indexer certificates created.
07/10/2026 23:01:55 INFO: Generating Filebeat certificates.
07/10/2026 23:01:55 INFO: Wazuh Filebeat certificates created.
07/10/2026 23:01:55 INFO: Generating Wazuh dashboard certificates.
07/10/2026 23:01:56 INFO: Wazuh dashboard certificates created.

The archive holds root-ca.key, which can sign a certificate any Wazuh component will trust. Copy wazuh-certificates.tar somewhere safe off the server before you go further, because you need it again for every node you add.

Add the Wazuh APT repository

All four packages come from one repository, so import the signing key and add the source once for the whole server:

curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | gpg --no-default-keyring --keyring gnupg-ring:/usr/share/keyrings/wazuh.gpg --import && chmod 644 /usr/share/keyrings/wazuh.gpg
echo "deb [signed-by=/usr/share/keyrings/wazuh.gpg] https://packages.wazuh.com/4.x/apt/ stable main" | tee /etc/apt/sources.list.d/wazuh.list
apt-get update

Confirm the candidate versions before installing anything, since the indexer, manager and dashboard must all match:

apt-cache policy wazuh-indexer wazuh-manager wazuh-dashboard filebeat | grep -E '^[a-z]|Candidate'

Filebeat stays on the OSS 7.10.2 build that Wazuh supports, while the three Wazuh packages share one version:

wazuh-indexer:
  Candidate: 4.14.8-1
wazuh-manager:
  Candidate: 4.14.8-1
wazuh-dashboard:
  Candidate: 4.14.8-1
filebeat:
  Candidate: 7.10.2-2

If the three Wazuh candidates show different versions, stop here and fix the repository before installing anything.

Install and initialize the Wazuh indexer

The indexer is the OpenSearch-based store for alerts and inventory, and it has to be running before anything else can connect to it. The package took 75 seconds to install on our VM:

apt-get -y install wazuh-indexer

The default opensearch.yml already names the node node-1 and lists it as the initial cluster manager, which matches our config.yml. The one value to change is network.host, which ships as 0.0.0.0. Pin it to the server IP so the indexer answers on the same address its certificate was issued for:

sed -i "s/^network.host: .*/network.host: \"${HOST_IP}\"/" /etc/wazuh-indexer/opensearch.yml
grep -E '^(network.host|node.name)' /etc/wazuh-indexer/opensearch.yml

Both lines should now read as follows, and the node name must match the name used in config.yml:

network.host: "192.168.1.138"
node.name: "node-1"

Unpack the certificate for this node, the admin certificate and the CA into the certs directory of the indexer. The renames matter, because the config expects the files to be called indexer.pem and indexer-key.pem:

NODE_NAME=node-1
mkdir /etc/wazuh-indexer/certs
tar -xf ./wazuh-certificates.tar -C /etc/wazuh-indexer/certs/ ./$NODE_NAME.pem ./$NODE_NAME-key.pem ./admin.pem ./admin-key.pem ./root-ca.pem
mv -n /etc/wazuh-indexer/certs/$NODE_NAME.pem /etc/wazuh-indexer/certs/indexer.pem
mv -n /etc/wazuh-indexer/certs/$NODE_NAME-key.pem /etc/wazuh-indexer/certs/indexer-key.pem
chmod 500 /etc/wazuh-indexer/certs
chmod 400 /etc/wazuh-indexer/certs/*
chown -R wazuh-indexer:wazuh-indexer /etc/wazuh-indexer/certs

Start the service, then run the security init script once. It loads the users, roles and certificate DNs into the cluster, and nothing can log in until it has run:

systemctl daemon-reload
systemctl enable --now wazuh-indexer
/usr/share/wazuh-indexer/bin/indexer-security-init.sh

It ends with Done with success. Query the node with the default admin:admin credentials, which we rotate later. If the first call returns OpenSearch Security not initialized., wait five seconds and try again; we got that once, straight after the init finished:

curl -sk -u admin:admin "https://${HOST_IP}:9200"
curl -sk -u admin:admin "https://${HOST_IP}:9200/_cat/nodes?v"

The version field reports 7.10.2 for Filebeat compatibility, and the node list shows one node holding every role:

{
  "name" : "node-1",
  "cluster_name" : "wazuh-cluster",
  "cluster_uuid" : "4uKsI1wbQFWyUoZ3XCvKjg",
  "version" : {
    "number" : "7.10.2",
    "build_type" : "deb",
    "build_hash" : "9f4796b8738969ac50ea8b6d23cdea5ce1a8565a",
    "build_date" : "2026-09-18T11:18:26.846334859Z",
    "build_snapshot" : false,
    "lucene_version" : "9.12.3",
    "minimum_wire_compatibility_version" : "7.10.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "The OpenSearch Project: https://opensearch.org/"
}
ip            heap.percent ram.percent cpu load_1m load_5m load_15m node.role node.roles                                        cluster_manager name
192.168.1.138           39          57  24    1.27    0.60     0.29 dimr      cluster_manager,data,ingest,remote_cluster_client *               node-1

The asterisk marks node-1 as the elected cluster manager, so the indexer is up as a cluster of one.

Install the Wazuh manager and Filebeat

The manager analyzes agent events and writes alerts, and Filebeat ships those alerts into the indexer. Install both:

apt-get -y install wazuh-manager
apt-get -y install filebeat

Wazuh publishes a ready Filebeat configuration for each release branch. Download it and point its output at the LAN address of the indexer instead of the default 127.0.0.1, which our indexer no longer listens on:

curl -so /etc/filebeat/filebeat.yml "https://packages.wazuh.com/${WAZUH_MINOR}/tpl/wazuh/filebeat/filebeat.yml"
sed -i "s/127.0.0.1:9200/${HOST_IP}:9200/" /etc/filebeat/filebeat.yml

After the edit, /etc/filebeat/filebeat.yml on our server read as follows:

# Wazuh - Filebeat configuration file
output.elasticsearch:
  hosts: ["192.168.1.138:9200"]
  protocol: https
  username: ${username}
  password: ${password}
  ssl.certificate_authorities:
    - /etc/filebeat/certs/root-ca.pem
  ssl.certificate: "/etc/filebeat/certs/filebeat.pem"
  ssl.key: "/etc/filebeat/certs/filebeat-key.pem"
setup.template.json.enabled: true
setup.template.json.path: '/etc/filebeat/wazuh-template.json'
setup.template.json.name: 'wazuh'
setup.ilm.overwrite: true
setup.ilm.enabled: false

filebeat.modules:
  - module: wazuh
    alerts:
      enabled: true
    archives:
      enabled: false

logging.level: info
logging.to_files: true
logging.files:
  path: /var/log/filebeat
  name: filebeat
  keepfiles: 7
  permissions: 0644

logging.metrics.enabled: false

seccomp:
  default_action: allow
  syscalls:
  - action: allow
    names:
    - rseq

The ${username} and ${password} placeholders resolve from a Filebeat keystore, so the credentials never sit in the YAML. Create it with the default credentials for now, then fetch the alerts index template for the exact manager version and install the Wazuh Filebeat module:

filebeat keystore create
echo admin | filebeat keystore add username --stdin --force
echo admin | filebeat keystore add password --stdin --force
curl -so /etc/filebeat/wazuh-template.json "https://raw.githubusercontent.com/wazuh/wazuh/v${WAZUH_VER}/extensions/elasticsearch/7.x/wazuh-template.json"
chmod go+r /etc/filebeat/wazuh-template.json
curl -s https://packages.wazuh.com/4.x/filebeat/wazuh-filebeat-0.5.tar.gz | tar -xvz -C /usr/share/filebeat/module

Filebeat authenticates to the indexer with the wazuh-1 certificate, renamed to the file names its config expects:

NODE_NAME=wazuh-1
mkdir /etc/filebeat/certs
tar -xf ./wazuh-certificates.tar -C /etc/filebeat/certs/ ./$NODE_NAME.pem ./$NODE_NAME-key.pem ./root-ca.pem
mv -n /etc/filebeat/certs/$NODE_NAME.pem /etc/filebeat/certs/filebeat.pem
mv -n /etc/filebeat/certs/$NODE_NAME-key.pem /etc/filebeat/certs/filebeat-key.pem
chmod 500 /etc/filebeat/certs
chmod 400 /etc/filebeat/certs/*
chown -R root:root /etc/filebeat/certs

The manager also talks to the indexer directly for vulnerability detection. It keeps its own keystore, and its ossec.conf points at https://0.0.0.0:9200 by default, which has to become the real indexer address:

echo admin | /var/ossec/bin/wazuh-keystore -f indexer -k username
echo admin | /var/ossec/bin/wazuh-keystore -f indexer -k password
sed -i "s|<host>https://0.0.0.0:9200</host>|<host>https://${HOST_IP}:9200</host>|" /var/ossec/etc/ossec.conf

Start both services and test the path from Filebeat to the indexer, which checks DNS, TLS and authentication in one go so that you do not have to debug them separately:

systemctl daemon-reload
systemctl enable --now wazuh-manager
systemctl enable --now filebeat
filebeat test output

Every stage should say OK, with full certificate chain verification enabled:

elasticsearch: https://192.168.1.138:9200...
  parse url... OK
  connection...
    parse host... OK
    dns lookup... OK
    addresses: 192.168.1.138
    dial up... OK
  TLS...
    security: server's certificate chain verification is enabled
    handshake... OK
    TLS version: TLSv1.3
    dial up... OK
  talk to server... OK
  version: 7.10.2

One oddity if you run /var/ossec/bin/wazuh-control info: the stable manager package reports WAZUH_REVISION="rc2". It appears to be a build revision carried into the final package rather than a sign you pulled a release candidate, and WAZUH_VERSION="v4.14.8" on the line above it is the one that counts.

Install and wire up the Wazuh dashboard

The dashboard is the last package and the slowest to start:

apt-get -y install wazuh-dashboard

Its certificate goes in the same way as the others. These are the lines from the Wazuh documentation verbatim, including the guards that skip the rename if the file already exists:

NODE_NAME=dashboard
mkdir /etc/wazuh-dashboard/certs
tar -xf ./wazuh-certificates.tar -C /etc/wazuh-dashboard/certs/ ./$NODE_NAME.pem ./$NODE_NAME-key.pem ./root-ca.pem
[ ! -e /etc/wazuh-dashboard/certs/dashboard.pem ] && mv -n /etc/wazuh-dashboard/certs/$NODE_NAME.pem /etc/wazuh-dashboard/certs/dashboard.pem
[ ! -e /etc/wazuh-dashboard/certs/dashboard-key.pem ] && mv -n /etc/wazuh-dashboard/certs/$NODE_NAME-key.pem /etc/wazuh-dashboard/certs/dashboard-key.pem
chmod 500 /etc/wazuh-dashboard/certs
chmod 400 /etc/wazuh-dashboard/certs/*
chown -R wazuh-dashboard:wazuh-dashboard /etc/wazuh-dashboard/certs

Before starting it, fix the indexer address, because the package ships with opensearch.hosts: https://localhost:9200, and because our indexer binds the LAN IP, a dashboard left on localhost only returns Wazuh dashboard server is not ready yet with HTTP 503. We left it on the default first to see exactly that, and the error is listed at the end. Set it to the server IP so the dashboard talks to the same address the certificate covers:

sed -i "s|^opensearch.hosts: .*|opensearch.hosts: https://${HOST_IP}:9200|" /etc/wazuh-dashboard/opensearch_dashboards.yml
grep ^opensearch.hosts /etc/wazuh-dashboard/opensearch_dashboards.yml

The line now carries the LAN address:

opensearch.hosts: https://192.168.1.138:9200

Start the dashboard next. The file that tells it where the manager API lives, wazuh.yml, only appears after the first start, so point it at the manager afterwards and restart once more:

systemctl daemon-reload
systemctl enable --now wazuh-dashboard
for i in $(seq 1 60); do [ -f /usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml ] && break; sleep 5; done
sed -i "s|url: https://localhost|url: https://${HOST_IP}|" /usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml
grep -E 'url:|port:' /usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml | grep -v '#'
systemctl restart wazuh-dashboard

The loop waits up to five minutes. If sed then reports that it cannot read wazuh.yml, the dashboard did not start, and journalctl -u wazuh-dashboard -n 50 shows why (a certificate permission or a typo in opensearch.hosts are the usual causes). The API entry now points at the manager on the LAN address:

      url: https://192.168.1.138
      port: 55000

The login page answered with HTTP 200 about 15 seconds after the restart on our VM.

All four packages are now installed. Disable the Wazuh repository so a routine apt upgrade cannot move the indexer, manager and dashboard out of step; you re-enable it on purpose when you plan an upgrade:

sed -i 's/^deb /#deb /' /etc/apt/sources.list.d/wazuh.list && apt-get update
grep wazuh /etc/apt/sources.list.d/wazuh.list

The grep shows the source line commented out:

#deb [signed-by=/usr/share/keyrings/wazuh.gpg] https://packages.wazuh.com/4.x/apt/ stable main

From now on an apt upgrade leaves the Wazuh packages alone until you uncomment that line.

Replace the default passwords

At this point the indexer admin account is still admin:admin and the API users carry their default passwords too, so this step is not optional on anything that is reachable from another machine. The passwords tool rotates every internal user and the two API users in one run, and the --admin-user wazuh --admin-password wazuh pair is the default API admin it authenticates with:

/usr/share/wazuh-indexer/plugins/opensearch-security/tools/wazuh-passwords-tool.sh --api --change-all --admin-user wazuh --admin-password wazuh

It prints each new password once, so copy them into your password manager immediately (masked here):

07/10/2026 23:15:35 INFO: Updating the internal users.
07/10/2026 23:15:38 INFO: A backup of the internal users has been saved in the /etc/wazuh-indexer/internalusers-backup folder.
07/10/2026 23:15:47 INFO: The filebeat.yml file has been updated to use the Filebeat Keystore username and password.
07/10/2026 23:16:08 INFO: The password for user admin is ********
07/10/2026 23:16:08 INFO: The password for user anomalyadmin is ********
07/10/2026 23:16:08 INFO: The password for user kibanaserver is ********
07/10/2026 23:16:08 INFO: The password for user kibanaro is ********
07/10/2026 23:16:08 INFO: The password for user logstash is ********
07/10/2026 23:16:08 INFO: The password for user readall is ********
07/10/2026 23:16:08 INFO: The password for user snapshotrestore is ********
07/10/2026 23:16:08 WARNING: Wazuh indexer passwords changed. Remember to update the password in the Wazuh dashboard, Wazuh server, and Filebeat nodes if necessary, and restart the services.
07/10/2026 23:16:11 INFO: The password for Wazuh API user wazuh is ********
07/10/2026 23:16:12 INFO: The password for Wazuh API user wazuh-wui is ********
07/10/2026 23:16:12 INFO: Updated wazuh-wui user password in wazuh dashboard. Remember to restart the service.

On a single node the tool also updates everything that stores those passwords: the Filebeat keystore, the manager keystore that vulnerability detection reads, and the dashboard keystore and wazuh-wui entry. We restarted the three services anyway so they pick up the new values together, then checked the cluster with the new admin password. Set ADMIN_PASS to the value the tool printed for admin, since later steps use it:

export ADMIN_PASS='paste-the-new-admin-password-here'
systemctl restart filebeat wazuh-manager wazuh-dashboard
curl -sk -u "admin:${ADMIN_PASS}" "https://${HOST_IP}:9200/_cluster/health?pretty" | grep -E 'status|number_of_nodes'
curl -sk -o /dev/null -w 'admin:admin -> %{http_code}\n' -u admin:admin "https://${HOST_IP}:9200"

The cluster is green under the new password, and the old default is refused:

  "status" : "green",
  "number_of_nodes" : 1,
admin:admin -> 401

If you want to manage the indexer users afterwards, our walkthrough on creating and updating OpenSearch users applies to the Wazuh indexer as well, since it is the same security plugin.

Open the firewall and log in

UFW is installed but inactive on Ubuntu 24.04, so allow SSH first to make sure enabling it does not cut your session, then allow the dashboard and the two agent ports. The OpenSSH profile covers port 22 only, so if SSH listens elsewhere add a line such as ufw allow 2222/tcp with your port before the enable, and open the same ports in any cloud security group in front of the server:

ufw allow OpenSSH
ufw allow 443/tcp comment 'Wazuh dashboard'
ufw allow 1514/tcp comment 'Wazuh agent events'
ufw allow 1515/tcp comment 'Wazuh agent enrollment'
ufw --force enable
ufw status

The status listing should show SSH plus the three Wazuh ports and nothing else:

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
443/tcp                    ALLOW       Anywhere                   # Wazuh dashboard
1514/tcp                   ALLOW       Anywhere                   # Wazuh agent events
1515/tcp                   ALLOW       Anywhere                   # Wazuh agent enrollment

Port 9200 and 55000 stay closed because every consumer of them lives on this host. When you split the indexer out later, open 9200 to the manager and dashboard nodes only, not to the world.

Browse to https://192.168.1.138 for now (after the certificate step you switch to the DNS name) and sign in as admin with the new password. Under Server management > Status you can confirm every daemon the single node needs is running. The red ones (cluster, mail, integrator, agentless, report, csyslog) are off by default and stay off until you enable those features:

Wazuh server status page on Ubuntu 24.04 showing manager v4.14.8 daemons and the last registered agent

That screenshot was taken after the agent from the next sections enrolled, and that is why it already shows one active agent.

Size the indexer heap and lock its memory

A fresh package install leaves the indexer at a 1 GB heap, which is fine for the first boot and nowhere near enough once agents are writing. The rule is half the RAM, Xms equal to Xmx, and the heap locked into memory so it never swaps. On our 8 GB server that’s 4 GB, which is also the largest value we would run next to the other components. Locking needs a systemd override as well as the setting in opensearch.yml:

sed -i '$a bootstrap.memory_lock: true' /etc/wazuh-indexer/opensearch.yml
mkdir -p /etc/systemd/system/wazuh-indexer.service.d/
printf '[Service]\nLimitMEMLOCK=infinity\n' | tee /etc/systemd/system/wazuh-indexer.service.d/wazuh-indexer.conf
sed -i -e 's/^-Xms1g/-Xms4g/' -e 's/^-Xmx1g/-Xmx4g/' /etc/wazuh-indexer/jvm.options
systemctl daemon-reload
systemctl restart wazuh-indexer

We use sed '$a' rather than echo ... >> on purpose. The shipped opensearch.yml has no trailing newline, so an append glues the new setting onto the last line and the indexer refuses to start. That cost us a failed restart, and the exact error is in the troubleshooting list. Once it’s back up, check that the lock took and the heap is the size you asked for:

curl -sk -u "admin:${ADMIN_PASS}" "https://${HOST_IP}:9200/_nodes?filter_path=**.mlockall&pretty"
curl -sk -u "admin:${ADMIN_PASS}" "https://${HOST_IP}:9200/_cat/nodes?v&h=name,heap.max,heap.percent,ram.percent"

Both read as expected, with the lock active and a 4 GB heap:

{
  "nodes" : {
    "CIXB1AQuQmCZf9iFbAWpRA" : {
      "process" : {
        "mlockall" : true
      }
    }
  }
}
name   heap.max heap.percent ram.percent
node-1      4gb           14          98

The cost angle: with the heap locked, free -m on our 8 GB host dropped from 5.3 GB available to 2.1 GB, and the resident memory of the indexer went from 1.6 GB to 4.9 GB, while the dashboard, wazuh-modulesd, Filebeat and wazuh-analysisd together held about 450 MB. That’s why we’d move to 16 GB well before the quickstart table says you have to.

Terminal showing the Wazuh indexer node with a 4 GB heap, a passing Filebeat output test and an active agent on Ubuntu 24.04

We took that capture at the end of the build, so it already shows the agent you enroll in a later section. The check combines the heap, the Filebeat path and the agent list in one place, and it is a health snapshot we would keep as a small script that runs after every upgrade.

Serve the dashboard with a Let’s Encrypt certificate

The dashboard certificate we generated is signed by our private CA, so browsers warn on every visit. Because the dashboard terminates TLS itself on 443 and nothing on the server uses port 80, certbot can run its own HTTP-01 listener and a deploy hook can copy the result into place. Set the DNS name first; its A record must already point at a public address of this server:

export SITE_DOMAIN="siem.example.com"

Open port 80, install certbot and request the certificate with your own email. If port 80 cannot be reached from the internet, skip to the DNS-01 alternative below instead:

ufw allow 80/tcp comment 'Lets Encrypt HTTP-01'
apt-get install -y certbot
certbot certonly --standalone -d "${SITE_DOMAIN}" --non-interactive --agree-tos -m [email protected]

This build names the dashboard files dashboard.pem and dashboard-key.pem, which you can confirm with grep ^server.ssl /etc/wazuh-dashboard/opensearch_dashboards.yml. Create a deploy hook that overwrites them and restarts the dashboard:

vim /etc/letsencrypt/renewal-hooks/deploy/wazuh-dashboard-cert.sh

Add the following:

#!/bin/bash
set -euo pipefail
CERT_DIR=/etc/wazuh-dashboard/certs
install -m 400 -o wazuh-dashboard -g wazuh-dashboard "${RENEWED_LINEAGE}/fullchain.pem" "${CERT_DIR}/dashboard.pem"
install -m 400 -o wazuh-dashboard -g wazuh-dashboard "${RENEWED_LINEAGE}/privkey.pem" "${CERT_DIR}/dashboard-key.pem"
systemctl restart wazuh-dashboard

Make it executable, run it once against the new certificate, and dry-run a renewal so you know the timer will work in 60 days:

chmod 755 /etc/letsencrypt/renewal-hooks/deploy/wazuh-dashboard-cert.sh
RENEWED_LINEAGE="/etc/letsencrypt/live/${SITE_DOMAIN}" /etc/letsencrypt/renewal-hooks/deploy/wazuh-dashboard-cert.sh
certbot renew --dry-run

Keep root-ca.pem where it is, since the dashboard still uses it to verify the indexer. Only the browser-facing certificate changes, so from now on open the dashboard as https://siem.example.com; the bare IP gives a name mismatch.

Alternative: DNS-01 when port 80 isn’t reachable

A server on a private network or behind NAT cannot answer HTTP-01, so certbot proves ownership through a DNS TXT record instead, and you can skip the port 80 rule. Set SITE_DOMAIN as above, then install the plugin for your DNS provider (python3-certbot-dns-cloudflare, -route53, -digitalocean, -google or -rfc2136 for your own BIND). The Cloudflare example below works the same way for the others with their package, flag and credentials file. Create a root-only directory for the credentials:

mkdir -p -m 700 /root/.secrets

Then the credentials file itself:

vim /root/.secrets/cloudflare.ini

It holds one line, an API token that can edit DNS for the zone:

dns_cloudflare_api_token = your-token-here

Lock it down and request the certificate with the plugin flag in place of --standalone:

chmod 600 /root/.secrets/cloudflare.ini
apt-get install -y python3-certbot-dns-cloudflare
certbot certonly --dns-cloudflare --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
  -d "${SITE_DOMAIN}" --non-interactive --agree-tos -m [email protected]

Then create the same deploy hook and run it once, exactly as in the HTTP-01 path. When DNS-01 fails, the cause is nearly always an API token that can’t edit the zone, whereas HTTP-01 failures come down to port 80 being blocked upstream or the A record not resolving yet.

Require a password for agent enrollment

By default wazuh-authd registers any machine that can reach port 1515, which on a flat network means anyone can add an agent and flood the manager. A shared enrollment password closes that. Generate one, store it where authd looks, and switch the option on:

openssl rand -hex 16 > /var/ossec/etc/authd.pass
chmod 640 /var/ossec/etc/authd.pass
chown root:wazuh /var/ossec/etc/authd.pass
sed -i 's|<use_password>no</use_password>|<use_password>yes</use_password>|' /var/ossec/etc/ossec.conf
systemctl restart wazuh-manager
grep -i "password" /var/ossec/logs/ossec.log | tail -2

The log shows authd before and after the change, and the second line is the one you want to see:

2026/10/07 23:16:37 wazuh-authd: INFO: Accepting connections on port 1515. No password required.
2026/10/07 23:26:17 wazuh-authd: INFO: Accepting connections on port 1515. Using password specified on file: etc/authd.pass

Run cat /var/ossec/etc/authd.pass and keep the value handy for the agent.

Enroll an Ubuntu agent with the password

Switch to the machine you want to monitor, because everything in this section happens on the agent. These commands run as a normal user with sudo. Pin the agent to the manager version, which you can read on the server with dpkg-query -W -f='${Version}\n' wazuh-manager (ours printed 4.14.8-1), then add the same repository and install the agent, passing the manager address and a name at install time:

export WAZUH_AGENT_VER="4.14.8-1" #https://github.com/wazuh/wazuh/releases
curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | sudo gpg --no-default-keyring --keyring gnupg-ring:/usr/share/keyrings/wazuh.gpg --import
sudo chmod 644 /usr/share/keyrings/wazuh.gpg
echo "deb [signed-by=/usr/share/keyrings/wazuh.gpg] https://packages.wazuh.com/4.x/apt/ stable main" | sudo tee /etc/apt/sources.list.d/wazuh.list
sudo apt-get update
sudo WAZUH_MANAGER="192.168.1.138" WAZUH_AGENT_NAME="app-eu-west-1a" apt-get install -y wazuh-agent="${WAZUH_AGENT_VER}"

The package does not start the agent, which gives you a window to drop the enrollment password in first, and that is exactly what we want here. Put the value from the manager into the authd.pass file on the agent, which uses the same path as the manager:

echo "paste-the-enrollment-password-here" | sudo tee /var/ossec/etc/authd.pass >/dev/null
sudo chmod 640 /var/ossec/etc/authd.pass
sudo chown root:wazuh /var/ossec/etc/authd.pass
sudo systemctl daemon-reload
sudo systemctl enable --now wazuh-agent

Within a few seconds the agent log shows the key exchange and the connection on 1514:

sudo grep -E "Valid key|Connected to the server" /var/ossec/logs/ossec.log | tail -2

The two lines you are looking for:

2026/10/07 23:27:53 wazuh-agentd: INFO: Valid key received
2026/10/07 23:28:13 wazuh-agentd: INFO: (4102): Connected to the server ([192.168.1.138]:1514/tcp).

Wazuh supports agents on the same version as the manager or older, never newer, so stop the agent from moving ahead on a routine upgrade by disabling its repository now:

sudo sed -i 's/^deb /#deb /' /etc/apt/sources.list.d/wazuh.list && sudo apt-get update

We started our first agent without the file on purpose, and it did not fail quietly: it retried every few seconds with Invalid password. Unable to add agent (from manager) until the file was in place, so a typo here fails loudly rather than silently. Under Agents management > Summary the dashboard lists the agent as active, with its operating system and version picked up from the first keepalive:

Wazuh endpoints list showing agent app-eu-west-1a active on Ubuntu 24.04

For more than a handful of hosts, the Ansible Wazuh deployment writes authd.pass and installs the agent across an inventory, and the Rocky Linux and AlmaLinux setup covers RPM-based agents.

Push file integrity monitoring from the manager

Syscheck can only watch and baseline a directory that exists, so create the test directory and a starting config file on the agent first:

sudo mkdir -p /opt/app/config
echo "db_host=10.0.2.15" | sudo tee /opt/app/config/app.ini

Editing ossec.conf on each agent does not scale past a few hosts, and it is easy for one host to drift from the rest. Agent groups solve it: anything in /var/ossec/etc/shared/default/agent.conf on the manager is synced to every agent in the default group. We’ll watch an application config directory in real time, with content diffs. Back on the manager, still in the root shell:

vim /var/ossec/etc/shared/default/agent.conf

Replace the placeholder content with the following block, which watches one directory:

<agent_config>
  <syscheck>
    <directories realtime="yes" check_all="yes" report_changes="yes">/opt/app/config</directories>
  </syscheck>
</agent_config>

Validate the file before the agents pull it, because a broken shared config can stop them reloading:

/var/ossec/bin/verify-agent-conf

A clean file prints OK:

verify-agent-conf: Verifying [etc/shared/default/agent.conf]
verify-agent-conf: OK

Our agent picked the change up about 15 seconds later and restarted its syscheck module on its own. Check the agent log to confirm the directory is under real-time monitoring:

sudo grep "/opt/app/config" /var/ossec/logs/ossec.log

Both lines should appear:

2026/10/07 23:28:56 wazuh-syscheckd: INFO: (6003): Monitoring path: '/opt/app/config', with options 'size | permissions | owner | group | mtime | inode | hash_md5 | hash_sha1 | hash_sha256 | report_changes | realtime'.
2026/10/07 23:28:56 wazuh-syscheckd: INFO: (6016): Directory set for real time monitoring: '/opt/app/config'.

Give the agent a few seconds to record its baseline, then change the existing file and create a new one:

echo "db_host=203.0.113.66" | sudo tee /opt/app/config/app.ini
echo "debug=true" | sudo tee /opt/app/config/override.ini

Both events reached the manager within a second. On the manager, pull them out of the current alerts.json:

grep -E '"id":"(550|554)"' /var/ossec/logs/alerts/alerts.json | python3 -c 'import sys,json; [print(a["timestamp"], a["agent"]["name"], a["rule"]["id"], a["rule"]["level"], a["syscheck"]["event"], a["syscheck"]["path"], repr(a["syscheck"].get("diff", ""))) for a in map(json.loads, sys.stdin)]' | tail -2

Rule 550 flags the modified file at level 7 and carries the diff, while rule 554 reports the new file at level 5:

2026-10-08T00:20:55.829+0000 app-eu-west-1a 550 7 modified /opt/app/config/app.ini '1c1\n< db_host=10.0.2.15\n---\n> db_host=203.0.113.66\n'
2026-10-08T00:20:55.871+0000 app-eu-west-1a 554 5 added /opt/app/config/override.ini ''

In the dashboard, open Endpoint security > File Integrity Monitoring and switch to the Events tab to see the same two changes. This capture is from our first FIM run, a few minutes before the command-line run above:

Wazuh file integrity monitoring events showing rule 550 modified and rule 554 added for /opt/app/config

Real-time mode uses inotify, so keep it on directories that change rarely. For busy paths, the default scheduled scan is cheaper on the agent.

Errors we hit during the manual install

[ConnectionError]: connect ECONNREFUSED ::1:9200

This filled the dashboard journal while the browser showed Wazuh dashboard server is not ready yet (HTTP 503). The dashboard still had the packaged opensearch.hosts: https://localhost:9200, localhost resolved to IPv6 ::1, and our indexer only listens on the LAN address. Setting opensearch.hosts to https://${HOST_IP}:9200 and restarting fixed it. The same applies to Filebeat and the manager <indexer> block, which is why we changed all three.

Caused by: mapping values are not allowed here

The indexer failed to start right after we enabled memory locking. The journal pointed at the end of the file:

Caused by: mapping values are not allowed here
 in 'reader', line 42, column 72:
     ... rsion: truebootstrap.memory_lock: true

We had appended the setting with tee -a, and because opensearch.yml ships without a trailing newline it landed on the same line as compatibility.override_main_response_version: true. Split the line by hand, or use sed -i '$a ...' as shown above, which adds the newline for you.

ERROR: Invalid password. Unable to add agent (from manager)

The agent log repeats this every few seconds when the manager requires a password and the agent has none, or a different one. Put the exact value from /var/ossec/etc/authd.pass on the manager into the same path on the agent and restart wazuh-agent; the next attempt prints Valid key received.

OpenSearch Security not initialized.

Returned once by our first curl to port 9200, issued in the same second that indexer-security-init.sh finished. It cleared on retry five seconds later, so treat it as a timing issue first. If it persists, the init script didn’t run or failed, so run it again and read its output.

Scaling path: from one node to a real cluster

The reason we built this by hand pays off here. When one server stops being enough, the indexer is almost always the first thing to outgrow it, because heap and disk grow with retention while manager CPU grows with event rate, and those rarely peak together. Our recommended order of operations, based on which part usually runs out of headroom first, looks like this:

  • Move the indexer first. Add a node-2 entry to config.yml, generate its certificate with the same CA, install wazuh-indexer on the new host and add it to discovery.seed_hosts, cluster.initial_master_nodes and plugins.security.nodes_dn. Point Filebeat, the manager <indexer> block and opensearch.hosts at the new addresses.
  • Go to three indexer nodes, not two. Two nodes cannot hold a quorum after one fails, so we’d skip straight to three identically sized nodes on a low-latency network, since the cluster runs at the pace of its slowest member.
  • Add manager workers when event rate climbs. Enable the <cluster> block with a shared 32-character key from openssl rand -hex 16, make this server the master, and put a TCP load balancer in front of 1514 and 1515 so agents spread across the nodes. Workers forward enrollment requests to the master, so 1515 can sit behind the same balancer.
  • Keep the dashboard light. It rarely needs its own box until you have many concurrent analysts, so it can stay with the master manager for a long time.

The cost angle is straightforward: three 16 GB indexer nodes plus a manager pair is roughly five times the spend of this single server, so we’d only make that move once retention or ingest has demonstrably outgrown one 16 GB node. Until then, the setup above with a bigger heap and a retention policy will carry a surprising number of agents, and if you also need log search outside Wazuh, Graylog on Ubuntu can share the same certificate habits. For the detection theory and SOC workflow this build leaves out, our picks of books on SIEM and Wazuh are where we’d start reading. Whichever way you grow, keep the repository commented out between planned upgrades and upgrade the indexer, then the manager, then the dashboard, with agents last.

Keep reading

Upgrade Ubuntu 24.04 to Ubuntu 26.04 LTS (Step by Step) Ubuntu Upgrade Ubuntu 24.04 to Ubuntu 26.04 LTS (Step by Step) UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 Security UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 PHP2Shell (CVE-2026-109618, CVE-2026-109619): Check and Patch PHP Php PHP2Shell (CVE-2026-109618, CVE-2026-109619): Check and Patch PHP Upgrade a Single Package on Ubuntu / Debian with apt Debian Upgrade a Single Package on Ubuntu / Debian with apt APT Cheat Sheet: apt and apt-get Commands for Ubuntu/Debian Cheat Sheets APT Cheat Sheet: apt and apt-get Commands for Ubuntu/Debian LVM Snapshots for Consistent Server Backups on Linux Backup LVM Snapshots for Consistent Server Backups on Linux

20 thoughts on “Install Wazuh Server on Ubuntu 24.04 LTS Step by Step”

  1. Very nice how to, but I did run into a problem. At the kibana install, after I downloaded the config, I ran
    sudo chown -R kibana:kibana /usr/share/kibana/optimize
    I get “chown: cannot access ‘/usr/share/kibana/optimize’: No such file or directory”
    But I think more importantly when I try in download the plugin with’
    cd /usr/share/kibana
    sudo -u kibana /usr/share/kibana/bin/kibana-plugin install https://packages.wazuh.com/4.x/ui/kibana/wazuh_kibana-4.0.4_7.9.1-1.zip
    I get,
    Plugin installation was unsuccessful due to error “No kibana plugins found in archive”
    Any thought on how to get this plugin installed?
    Thanks

    Reply
  2. For me, “sudo systemctl enable –now elasticsearch” results in:

    Synchronizing state of elasticsearch.service with SysV service script with /lib/systemd/systemd-sysv-install.
    Executing: /lib/systemd/systemd-sysv-install enable elasticsearch
    Job for elasticsearch.service failed because a timeout was exceeded.
    See “systemctl status elasticsearch.service” and “journalctl -xe” for details.

    Reply
  3. HI,

    I am getting following error

    wazuh@wazuh:/etc/elasticsearch/certs$ sudo systemctl enable –now elasticsearch Synchronizing state of elasticsearch.service with SysV service script with /lib/systemd/systemd-sysv-install.
    Executing: /lib/systemd/systemd-sysv-install enable elasticsearch
    Job for elasticsearch.service failed because a timeout was exceeded.
    See “systemctl status elasticsearch.service” and “journalctl -xe” for details.
    wazuh@wazuh:/etc/elasticsearch/certs$ systemctl status elasticsearch.service
    ● elasticsearch.service – Elasticsearch
    Loaded: loaded (/lib/systemd/system/elasticsearch.service; enabled; vendor preset: enabled)
    Active: failed (Result: timeout) since Tue 2022-02-15 04:57:03 UTC; 16s ago
    Docs: https://www.elastic.co
    Process: 77555 ExecStart=/usr/share/elasticsearch/bin/systemd-entrypoint -p ${PID_DIR}/elasticsearch.pid –quiet (code=exited, sta>
    Main PID: 77555 (code=exited, status=143)

    Feb 15 04:55:57 wazuh systemd-entrypoint[77555]: at org.elasticsearch.bootstrap.Elasticsearch.execute(Elasticsearch.java:161)
    Feb 15 04:55:57 wazuh systemd-entrypoint[77555]: at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareComm>
    Feb 15 04:55:57 wazuh systemd-entrypoint[77555]: at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:127)
    Feb 15 04:55:57 wazuh systemd-entrypoint[77555]: at org.elasticsearch.cli.Command.main(Command.java:90)
    Feb 15 04:55:57 wazuh systemd-entrypoint[77555]: at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:126)
    Feb 15 04:55:57 wazuh systemd-entrypoint[77555]: at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:92)
    Feb 15 04:55:57 wazuh systemd-entrypoint[77555]: For complete error details, refer to the log at /var/log/elasticsearch/elasticsearch.>
    Feb 15 04:57:03 wazuh systemd[1]: elasticsearch.service: start operation timed out. Terminating.
    Feb 15 04:57:03 wazuh systemd[1]: elasticsearch.service: Failed with result ‘timeout’.
    Feb 15 04:57:03 wazuh systemd[1]: Failed to start Elasticsearch.
    lines 1-17/17 (END)
    ● elasticsearch.service – Elasticsearch
    Loaded: loaded (/lib/systemd/system/elasticsearch.service; enabled; vendor preset: enabled)
    Active: failed (Result: timeout) since Tue 2022-02-15 04:57:03 UTC; 16s ago
    Docs: https://www.elastic.co
    Process: 77555 ExecStart=/usr/share/elasticsearch/bin/systemd-entrypoint -p ${PID_DIR}/elasticsearch.pid –quiet (code=exited, stat>
    Main PID: 77555 (code=exited, status=143)

    Feb 15 04:55:57 wazuh systemd-entrypoint[77555]: at org.elasticsearch.bootstrap.Elasticsearch.execute(Elasticsearch.java:161)
    Feb 15 04:55:57 wazuh systemd-entrypoint[77555]: at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareComma>
    Feb 15 04:55:57 wazuh systemd-entrypoint[77555]: at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:127)
    Feb 15 04:55:57 wazuh systemd-entrypoint[77555]: at org.elasticsearch.cli.Command.main(Command.java:90)
    Feb 15 04:55:57 wazuh systemd-entrypoint[77555]: at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:126)
    Feb 15 04:55:57 wazuh systemd-entrypoint[77555]: at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:92)
    Feb 15 04:55:57 wazuh systemd-entrypoint[77555]: For complete error details, refer to the log at /var/log/elasticsearch/elasticsearch.l>
    Feb 15 04:57:03 wazuh systemd[1]: elasticsearch.service: start operation timed out. Terminating.
    Feb 15 04:57:03 wazuh systemd[1]: elasticsearch.service: Failed with result ‘timeout’.
    Feb 15 04:57:03 wazuh systemd[1]: Failed to start Elasticsearch.

    Reply
  4. Hi,

    I am getting following error

    wazuhadmin@wazuh:/home$ sudo mkdir /etc/elasticsearch/certs && cd /etc/elasticsearch/certs
    bash: cd: /etc/elasticsearch/certs: Permission denied
    wazuhadmn@wazuh:/home$

    Thank you

    Reply
    • Seguramente que elasticsearch no está activo:
      sudo systemctl status elasticsearch
      Si sale inactivo o con algún error entonces:
      sudo systemctl restart elasticsearch
      Y ya podrá acceder a la interfaz web
      Un saludo

      Reply
  5. I get this error: INFO: Checking API host id [default]…
    INFO: Could not connect to API id [default]: 3099 – ERROR3099 – Invalid credentials

    Reply
  6. I get to:
    Run the Elasticsearch securityadmin script to load the new certificates information and start the cluster:

    export JAVA_HOME=/usr/share/elasticsearch/jdk/ && /usr/share/elasticsearch/plugins/opendistro_security/tools/securityadmin.sh -cd /usr/share/elasticsearch/plugins/opendistro_security/securityconfig/ -nhnv -cacert /etc/elasticsearch/certs/root-ca.pem -cert /etc/elasticsearch/certs/admin.pem -key /etc/elasticsearch/certs/admin-key.pem

    And get:
    Open Distro Security Admin v7
    Will connect to localhost:9300 … done
    18:49:26.405 [elasticsearch[_client_][transport_worker][T#1]] ERROR com.amazon.opendistroforelasticsearch.security.ssl.transport.OpenDistroSecuritySSLNettyTransport – Exception during establishing a SSL connection: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.ssl.Alert.createSSLException(Alert.java:131) ~[?:?]
    at sun.security.ssl.TransportContext.fatal(TransportContext.java:369) ~[?:?]
    at sun.security.ssl.TransportContext.fatal(TransportContext.java:312) ~[?:?]
    at sun.security.ssl.TransportContext.fatal(TransportContext.java:307) ~[?:?]
    at sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1357) ~[?:?]
    at sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1232) ~[?:?]
    at sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1175) ~[?:?]
    at sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:396) ~[?:?]
    at sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:480) ~[?:?]
    at sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1267) ~[?:?]
    at sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1254) ~[?:?]
    at java.security.AccessController.doPrivileged(AccessController.java:691) ~[?:?]
    at sun.security.ssl.SSLEngineImpl$DelegatedTask.run(SSLEngineImpl.java:1199) ~[?:?]
    at io.netty.handler.ssl.SslHandler.runAllDelegatedTasks(SslHandler.java:1542) ~[netty-handler-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.handler.ssl.SslHandler.runDelegatedTasks(SslHandler.java:1556) ~[netty-handler-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1440) ~[netty-handler-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1267) ~[netty-handler-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1314) ~[netty-handler-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:501) ~[netty-codec-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:440) ~[netty-codec-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276) ~[netty-codec-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) [netty-transport-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) [netty-transport-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) [netty-transport-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) [netty-transport-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) [netty-transport-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) [netty-transport-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) [netty-transport-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163) [netty-transport-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714) [netty-transport-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650) [netty-transport-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576) [netty-transport-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) [netty-transport-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) [netty-common-4.1.49.Final.jar:4.1.49.Final]
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) [netty-common-4.1.49.Final.jar:4.1.49.Final]
    at java.lang.Thread.run(Thread.java:832) [?:?]
    Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:439) ~[?:?]
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:306) ~[?:?]
    at sun.security.validator.Validator.validate(Validator.java:264) ~[?:?]
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:285) ~[?:?]
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:144) ~[?:?]
    at sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1335) ~[?:?]
    … 31 more
    Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141) ~[?:?]
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126) ~[?:?]
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297) ~[?:?]
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:434) ~[?:?]
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:306) ~[?:?]
    at sun.security.validator.Validator.validate(Validator.java:264) ~[?:?]
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:285) ~[?:?]
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:144) ~[?:?]
    at sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1335) ~[?:?]
    … 31 more
    ERR: Cannot connect to Elasticsearch. Please refer to elasticsearch logfile for more information
    Trace:
    NoNodeAvailableException[None of the configured nodes are available: [{#transport#-1}{-efRmOx6SQmGbg5Z3hRirA}{localhost}{127.0.0.1:9300}]]
    at org.elasticsearch.client.transport.TransportClientNodesService.ensureNodesAreAvailable(TransportClientNodesService.java:352)
    at org.elasticsearch.client.transport.TransportClientNodesService.execute(TransportClientNodesService.java:248)
    at org.elasticsearch.client.transport.TransportProxyClient.execute(TransportProxyClient.java:57)
    at org.elasticsearch.client.transport.TransportClient.doExecute(TransportClient.java:391)
    at org.elasticsearch.client.support.AbstractClient.execute(AbstractClient.java:412)
    at org.elasticsearch.client.support.AbstractClient.execute(AbstractClient.java:401)
    at com.amazon.opendistroforelasticsearch.security.tools.OpenDistroSecurityAdmin.execute(OpenDistroSecurityAdmin.java:524)
    at com.amazon.opendistroforelasticsearch.security.tools.OpenDistroSecurityAdmin.main(OpenDistroSecurityAdmin.java:157)

    Reply
  7. I am also getting an error when trying to start elastic search daemon. I never liked elastic search, it is a pain to get it up and running no matter in what format.

    × elasticsearch.service – Elasticsearch
    Loaded: loaded (/lib/systemd/system/elasticsearch.service; enabled; preset:
    enabled)
    Active: failed (Result: timeout) since Sat 2024-04-13 15:14:09 CEST; 16s ago
    Docs: https://www.elastic.co
    Process: 1339431 ExecStart=/usr/share/elasticsearch/bin/systemd-entrypoint -p ${PID_DIR}/elasticsearch.pid –quiet (code=exited, status=143)
    Main PID: 1339431 (code=exited, status=143)
    CPU: 30.801s

    Apr 13 15:13:02 systemd-entrypoint[1339431]: at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86)
    Apr 13 15:13:02 systemd-entrypoint[1339431]: at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:127)
    Apr 13 15:13:02 systemd-entrypoint[1339431]: at org.elasticsearch.cli.Command.main(Command.java:90)
    Apr 13 15:13:02 systemd-entrypoint[1339431]: at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:126)
    Apr 13 15:13:02 systemd-entrypoint[1339431]: at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:92)
    Apr 13 15:13:02 systemd-entrypoint[1339431]: For complete error details, refer to the log at /var/log/elasticsearch/elasticsearch.log
    Apr 13 15:14:09 systemd[1]: elasticsearch.service: start operation timed out. Terminating.
    Apr 13 15:14:09 systemd[1]: elasticsearch.service: Failed with result ‘timeout’.
    Apr 13 15:14:09 systemd[1]: Failed to start elasticsearch.service – Elasticsearch.
    Apr 13 15:14:09 systemd[1]: elasticsearch.service: Consumed 30.801s CPU time.

    Reply

Leave a Comment

Press ESC to close