Wazuh Docker (single node): installation, certificates, and definitive troubleshooting
Back to blog

Wazuh Docker (single node): installation, certificates, and definitive troubleshooting

6/7/2026 · 7 min · Cybersecurity

Wazuh is an open-source platform that combines XDR (Extended Detection and Response) and SIEM (Security Information and Event Management) capabilities. In this guide, I will show you how to deploy a single-node setup with Docker, configure your first agent on an Oracle Cloud (OCI) instance, properly secure the admin password, and resolve the most common field issues.


1. Prerequisites and required ports#

Before starting the installation process, validate that your server's firewall and your cloud provider's Security Lists (e.g. OCI) allow connections on the following ports:

PortProtocolServiceDescription
1514TCPWazuh AgentAgent communication
1515TCPEnrollmentNew agent registration
514UDPSyslogSyslog log collection
55000TCPManager APIInteraction with manager API
9200TCPIndexer APICommunication with indexer
4443TCPDashboardHTTPS web interface (custom port)
Infrastructure Tip: The default Dashboard port is 443, but we map it to 4443 on the host to avoid conflicts with Nginx or Apache already running.

To test port availability and audit host firewall status before deployment, run:

# Check if ports are listening on the host
ss -lntp | grep -E "1514|1515|9200|4443|55000"

# Validate UFW rules (if active)
sudo ufw status
# OR check Firewalld rules
sudo firewall-cmd --list-all

# Test local connectivity
nc -zv localhost 4443

Network flow and ports diagram#

To understand the packet flow between the agent, the internal Wazuh server stack, and administrators, check the communication diagram below:

flowchart TD subgraph Cliente [Wazuh Agent / Client Host] A[wazuh-agent] end subgraph Servidor [Wazuh Server Stack / Docker Host] M[Wazuh Manager] I[Wazuh Indexer] D[Wazuh Dashboard] end subgraph Admin [Administrator] C[Web Browser] end A -- "1514/TCP (Log forwarding)" --> M A -- "1515/TCP (Agent enrollment)" --> M M -- "9200/TCP (Indexer API)" --> I D -- "9200/TCP" --> I C -- "4443/TCP (HTTPS Dashboard)" --> D

2. Docker engine prerequisites verification (finding #1 & #7)#

The Wazuh container stack requires an updated and stable version of Docker Engine and the Docker Compose utility to run without YAML parsing errors. Verify the local environment before starting operations:

# Validate installed Docker version
docker --version

# Validate installed Docker Compose version
docker compose version

# Check the running status of the Docker daemon on the host
systemctl status docker --no-pager

# Ensure your administrative user is added to the docker group
groups $USER | grep docker

# Check for pending updates to Docker packages
apt list --upgradable 2>/dev/null | grep docker
# OR on CentOS/RHEL/CloudLinux
yum check-update | grep docker

3. Preventive planning and environment backups (finding #4)#

If you are implementing modifications in a development setup or performing Wazuh upgrades, it is essential to perform backups of existing configurations and network rules.

Run the commands from the project root directory (e.g. /opt/docker/wazuh/wazuh-docker/single-node/):

# Create a timestamped backup of the docker-compose.yml file
cp docker-compose.yml /root/docker-compose.yml.bak.$(date +%Y%m%d)

# Create a backup of custom tool configurations
cp -r config/ /root/config-backup-$(date +%Y%m%d)/

# Create a backup of previously generated TLS certificates
cp -r wazuh-certificates/ /root/certificates-backup-$(date +%Y%m%d)/

This backup policy protects your data (alerts, local rules, and security certificates) before starting major upgrades.


4. Resource and disk space auditing (finding #5)#

The Wazuh Indexer is intensive in its consumption of disk I/O resources and virtual memory. A lack of available space will halt log indexing immediately (causing a data Shard block).

# Check free disk space of the primary partition
df -h /

# Audit storage space consumed by orphan volumes or Docker images
docker system df

# Analyze the total size utilized by the Docker physical volumes directory
du -sh /var/lib/docker/volumes/

Ensure you have at least 20GB of free space for small lab environments and scale up storage proportionally to the volume of logs retained.


To simplify and speed up deployment, I developed an interactive Shell script that automates this entire guide (both the single-node server and the agent). The script handles:

GitHub: sr00t3d/wazuh-install

# Option A: Download and run
curl -O https://raw.githubusercontent.com/sr00t3d/wazuh-installer/refs/heads/main/wazuh-install.sh && chmod +x wazuh-install.sh && sudo ./wazuh-install.sh

# Option B: Direct URL execution
curl -sSL https://raw.githubusercontent.com/sr00t3d/wazuh-installer/refs/heads/main/wazuh-install.sh | sudo bash

# Non-interactive mode (CI/CD, cloud-init, Ansible):
sudo WAZUH_MODE=server WAZUH_ADMIN_PASS='MyStr0ngPass!' ./wazuh-install.sh --unattended
sudo WAZUH_MODE=agent WAZUH_MANAGER_IP=10.0.0.10 WAZUH_AGENT_NAME=webserver01 ./wazuh-install.sh --unattended

# View full help:
sudo ./wazuh-install.sh --help

6. Preparing the Docker environment (manual installation)#

If you opt for a purely manual stack deployment:

# Create dedicated working directory
mkdir -p /opt/docker/wazuh
cd /opt/docker/wazuh

# Clone official repository (version 4.14.3)
git clone https://github.com/wazuh/wazuh-docker.git -b v4.14.3
cd wazuh-docker/single-node/

7. Hardening: configuring a secure password before starting the stack#

Configure your custom password so containers initialize securely:

  1. Generate the BCrypt hash of your new password:
    docker run --rm wazuh/wazuh-indexer:4.14.3 \
      bash /usr/share/wazuh-indexer/plugins/opensearch-security/tools/hash.sh -p 'YourStrongPassword!'
  1. Replace the hash in the config/wazuh_indexer/internal_users.yml file under the admin user entry.
  2. Replace all occurrences of SecretPassword in the docker-compose.yml file.
  3. Start the stack:
    docker compose up -d

Method b: with the stack already running#

  1. Generate the hash inside the active container:
    docker exec -it single-node-wazuh.indexer-1 \
      /usr/share/wazuh-indexer/plugins/opensearch-security/tools/hash.sh -p 'YourStrongPassword!'
  1. Replace the hash in config/wazuh_indexer/internal_users.yml and SecretPassword in docker-compose.yml.
  2. Restart:
    docker compose down && docker compose up -d

8. Generating certificates and ssl/tls validation (finding #3)#

Wazuh requires certificates for secure communication between the indexer, dashboard, and server:

docker compose -f generate-indexer-certs.yml run --rm generator

After running the certificate generator, validate that the cryptographic layout and .pem files were properly created, and that expiration times are valid:

# Validate if certificates in PEM format were written
ls -la wazuh-certificates/*.pem

# Test if the local dashboard responds under HTTPS on port 4443
curl -k -I https://localhost:4443/

# Inspect start and expiration dates of the active TLS certificate
echo | openssl s_client -connect localhost:4443 2>/dev/null | openssl x509 -noout -dates

The openssl command above will extract and display notBefore and notAfter fields of the cryptographic keys in real time, helping identify expired certificates that would halt agent authentication.


9. Starting the stack and initialization validation#

Optional HTTPS port adjustment in the compose file:

sed -i 's/443:5601/4443:5601/g' docker-compose.yml

Start the services:

docker compose up -d
docker ps

Wait for images to initialize:

[+] Running 17/17
 ✔ Volume "single-node_filebeat_var"             Created    0.0s 
 ✔ Volume "single-node_wazuh_agentless"          Created    0.0s 
 ✔ Volume "single-node_wazuh_active_response"    Created    0.0s 
 ✔ Volume "single-node_wazuh-indexer-data"       Created    0.0s 
 ✔ Volume "single-node_wazuh-dashboard-custom"   Created    0.0s
 ✔ Volume "single-node_wazuh_api_configuration"  Created    0.0s 
 ✔ Volume "single-node_wazuh_integrations"       Created    0.0s 
 ✔ Volume "single-node_wazuh_queue"              Created    0.0s  
 ✔ Volume "single-node_filebeat_etc"             Created    0.0s 
 ✔ Volume "single-node_wazuh_wodles"             Created    0.0s 
 ✔ Volume "single-node_wazuh_var_multigroups"    Created    0.0s  
 ✔ Volume "single-node_wazuh-dashboard-config"   Created    0.0s 
 ✔ Volume "single-node_wazuh_etc"                Created    0.0s 
 ✔ Volume "single-node_wazuh_logs"               Created    0.0s  
 ✔ Container single-node-wazuh.indexer-1         Started    1.1s  
 ✔ Container single-node-wazuh.manager-1         Started    1.3s 
 ✔ Container single-node-wazuh.dashboard-1       Started    0.9s  

If everything is correct, navigate to https://YOUR_IP:4443 in your web browser. Log in with the password configured during the hardening step.


10. Auditing indexer readiness (finding #2)#

Before attempting to enroll monitoring agents or navigating the Wazuh dashboard, you must validate that the Indexer backend database (custom Elasticsearch/OpenSearch) is ready to accept write requests:

# Check if the indexer container is listed and active in Docker
docker ps | grep indexer

# Verify the basic response from the Indexer API on port 9200
curl -k https://localhost:9200/

# Audit the logical health status of the search cluster integrity
curl -k https://localhost:9200/_cluster/health

# Audit the last 50 lines of debug logs from the indexer
docker logs single-node-wazuh.indexer-1 --tail 50

If the cluster health (_cluster/health) returns a status of red, the indexer has corrupted partitions or shards, preventing login to the administrative dashboard. Make sure the basic curl response returns the JSON with the active indexer version.


11. Performance monitoring and telemetry (finding #8)#

Preventive operational monitoring prevents unexpected outages caused by indexing spikes or memory leaks (OOM) on the server host. Use the following tools to collect host and container telemetry:

# Display real-time CPU and Memory RAM consumption of each active container
docker stats --no-stream

# Check overall utilization of the host's physical processor and active processes
top -bn1 | head -10

# Collect a quick health summary of indexer search partitions
curl -k https://localhost:9200/_cat/health

Monitor that the indexer's Java Virtual Machine (JVM) allocated memory is not hitting the physical heap limits defined in the Docker Compose configuration file.


12. Forensic analysis of logs and exceptions (finding #9)#

In cases of incidents or intermittent instabilities, the SysAdmin should perform an audit on event files searching for Java error signatures and Elasticsearch/OpenSearch stack failures:

# Tailing logs showing the last 100 lines of the indexer
docker logs single-node-wazuh.indexer-1 --tail 100

# Filter the dynamic log searching for major errors or critical runtime exceptions
docker logs single-node-wazuh.indexer-1 2>&1 | grep -i -E "error|exception|fail"

# Display formatted details of cluster health in a user-friendly way
curl -k "https://localhost:9200/_cluster/health?pretty"

Searching for terms like OutOfMemoryError or ClusterBlockException indicates serious hardware bottlenecks or write blocks on disk due to a lack of physical space.


13. Agent deployment and connectivity validation (finding #10)#

In the Wazuh Dashboard, go to Deploy new agent, select the operating system, and copy the generated command. Example for Oracle Linux/RHEL:

curl -o wazuh-agent-4.14.3-1.x86_64.rpm https://packages.wazuh.com/4.x/yum/wazuh-agent-4.14.3-1.x86_64.rpm && \
sudo WAZUH_MANAGER='YOUR_IP_OR_DOMAIN' WAZUH_AGENT_GROUP='default' WAZUH_AGENT_NAME='machine01' \
rpm -ihv wazuh-agent-4.14.3-1.x86_64.rpm --force

Service startup#

sudo systemctl daemon-reload
sudo systemctl enable wazuh-agent
sudo systemctl start wazuh-agent
sudo systemctl status wazuh-agent

After installing and starting the agent on the client machine, validate the integrity and persistence of the connection to the Wazuh Manager:

# Verify if the agent daemon service is active and running on the client
systemctl status wazuh-agent --no-pager

# Read the OSSEC log file for success or error messages
tail -50 /var/ossec/logs/ossec.log

# Filter the log for successful connections attempts and status occurrences
grep -i -E "connected|connection|active" /var/ossec/logs/ossec.log | tail -5

# Test socket connectivity directly against manager port 1514
nc -zv YOUR_MANAGER_IP 1514

If the log contains the entry Wazuh Agent connected to Manager, the process was completed, and the agent is ready to monitor events.


14. Critical certificate troubleshooting (common errors)#

Two errors appear frequently in the field:

  1. not a directory on certificate bind mounts;
  2. Non-string key at top level: 404 when running the generation compose command.

Error 1: not a directory#

This usually happens when the expected host path for a file does not exist yet, and Docker creates a directory in its place.

Quick validation:

ls -ld config/wazuh_indexer_ssl_certs/*.pem

Error 2: 404 masquerading as YAML#

If the downloaded file is HTML (404) and not a real YAML file, the compose command fails with a parsing error.

Validate before running:

head -n 5 generate-indexer-certs.yml

Safe recovery flow#

  1. stop the stack;
  2. clean the broken state;
  3. clone the correct tag of wazuh-docker;
  4. regenerate certificates on the same version as the stack.
docker compose down
rm -rf wazuh-certificates/
git clone https://github.com/wazuh/wazuh-docker.git -b v4.14.3 wazuh-docker-clean

15. Updating Wazuh (without losing data)#

When a new version of Wazuh is released, follow this flow to update while preserving indexer data:

cd /opt/docker/wazuh

# 1. Download the new version of the repository (e.g. v4.14.4)
git clone https://github.com/wazuh/wazuh-docker.git -b v4.14.4 wazuh-docker-new
cd wazuh-docker-new/single-node/

# 2. Copy configurations and certificates from the previous version
cp -r ../../wazuh-docker/single-node/config ./
cp -r ../../wazuh-docker/single-node/wazuh-certificates ./

# 3. Stop the previous stack
(cd ../../wazuh-docker/single-node && docker compose down)

# 4. Start the new version (without -v to preserve volumes)
docker compose up -d

16. Wazuh Docker installation checklist#

Follow this operational checklist for stack deployment and validation:

Phase 1: Prerequisites & capacity#

Phase 2: Installation & hardening#

Phase 3: Server validation#

Phase 4: Agent enrollment & connectivity#


17. Risk and mitigation matrix#

Item / RiskSeverityTechnical DescriptionMitigation Measure
Default PasswordsCriticalInitializing the stack with the credential SecretPassword exposes the administrative API to immediate compromise.Replace the password hash with a robust BCrypt hash before the first bootstrap of the stack.
RAM Exhaustion (OOM)HighThe Indexer failing or crashing due to a lack of host OS memory or JVM Heap limits.Configure ZRAM or support Swap. Allocate explicit JVM Heap limits in the Docker Compose file.
Data Loss on UpgradeHighUsing the -v flag when shutting down containers deletes the volumes containing collected logs.Never run docker compose down -v in production audit environments.
SSL/Certificate ErrorsMediumHostname mismatches (SAN) or expired keys that prevent secure daemon connections.Validate key expiration with openssl s_client periodically and regenerate certificates on the same release tag.
Disk I/O BottleneckMediumMassive log writes saturating the physical write queue of cloud servers.Prefer fast SSD/NVMe storage and enable index expiration policies (Index Lifecycle Management).

Production takeaways#

Comprehensive technical article with complete installation guide for Wazuh Docker single-node. The explanation about ports, BCrypt certificates, and password hardening is precise and important - the warning about the default password SecretPassword is a security differential. The automated script on GitHub is a valuable professional resource that reduces human error. The certificate troubleshooting (errors not a directory and YAML 404) addresses real and common problems that many administrators face. The identified gaps (backup, Docker version verification, monitoring) are complementary and do not compromise the technical quality of the content.

Was this article helpful?

Leave a quick reaction to help prioritize future technical guides:

CC BY-NC

This post is licensed under CC BY-NC.

Comments

Join the discussion below.

0 comments