Deploying Wazuh on Kubernetes
Walk through the Wazuh Kubernetes deployment in a guided SOC lab format: clone the deployment repository, generate certificates, validate storage, apply the local manifests, verify the platform, access the dashboard, and document what was built.
Domain alignment
What this lesson covers
- Cloning the Wazuh Kubernetes deployment repository
- Understanding the local Kubernetes deployment layout
- Preparing certificate scripts on Windows / PowerShell
- Generating Wazuh indexer and dashboard certificates
- Checking Kubernetes storage before deploying a stateful platform
- Applying Wazuh manifests with Kustomize
- Validating namespaces, services, pods, PVCs, and events
- Accessing the dashboard with port-forwarding
- Reviewing default credentials and password-change expectations
- Understanding expected kubectl apply namespace warnings
- Troubleshooting common deployment failures
Before you start
This lesson assumes you already completed the earlier preparation lesson and have a working Kubernetes lab with kubectl, working storage, enough CPU and memory, Git, and a shell that can run Bash scripts.
Keep the current Wazuh Kubernetes deployment documentation open while doing the lab. The training below is written in Stealth IT Group style, but Wazuh version branches, manifests, resource names, service ports, and credentials may change over time.
Step 1: Confirm Kubernetes is ready
Do not start by applying manifests. First make sure the cluster is healthy enough to host a stateful SOC platform.
kubectl config current-context kubectl get nodes kubectl get pods -A kubectl get storageclass
What you are checking
- Context: kubectl is pointed at the lab cluster you intend to use.
- Nodes: the cluster has Ready nodes.
- System pods: the cluster is not already unhealthy.
- StorageClass: the cluster can dynamically provision storage for stateful Wazuh components.
Step 2: Clone the Wazuh Kubernetes deployment repository
Wazuh publishes Kubernetes deployment manifests in the wazuh-kubernetes repository. Clone the current versioned branch shown in the Wazuh documentation. At the time this page was prepared, the current Wazuh docs used v4.14.5.
git clone https://github.com/wazuh/wazuh-kubernetes.git -b v4.14.5 --depth=1 cd wazuh-kubernetes
What this does
This pulls the Kubernetes manifests, Kustomize overlays, certificate scripts, secrets, services, and workload definitions Wazuh uses to deploy its Kubernetes components.
Step 3: Review the repository layout
Before applying anything, inspect the directories. This builds operator confidence and helps later troubleshooting.
ls ls envs ls envs/local-env ls wazuh ls wazuh/certs ls wazuh/secrets
What to notice
- envs/local-env/ is the local Kubernetes deployment overlay.
- envs/eks/ is for Amazon EKS deployments.
- wazuh/certs/ contains certificate generation material.
- wazuh/secrets/ contains secret manifests that deserve review.
- Kustomize is used through kubectl apply -k.
Step 4: Prepare certificate scripts on Windows / PowerShell
If you cloned the Wazuh Kubernetes repository on Windows and are running the certificate scripts through WSL, confirm the scripts use Linux line endings before generating certificates. If the files have Windows CRLF line endings, Bash may fail with errors such as $'\r': command not found, broken cd paths, or OpenSSL parsing errors.
From PowerShell, while you are in C:\k8s\wazuh-kubernetes, run:
wsl bash -lc "cd /mnt/c/k8s/wazuh-kubernetes && sudo apt-get update && sudo apt-get install -y dos2unix && find wazuh/certs -type f \( -name '*.sh' -o -name '*.cnf' -o -name '*.conf' -o -name '*.yml' -o -name '*.yaml' \) -print0 | xargs -0 dos2unix"
If WSL asks for a password, it is asking for the Linux user password for your WSL distribution. It is not your Kubernetes password, not your Wazuh password, and not the Wazuh dashboard password.
Clean up failed partial certificate output
If you already tried to run the scripts and saw $'\r' errors, clean up any partial certificate files before rerunning the scripts.
wsl bash -lc "cd /mnt/c/k8s/wazuh-kubernetes/wazuh/certs/indexer_cluster && rm -f *.pem *.csr *.srl *-temp.pem && cd ../dashboard_http && rm -f *.pem *.csr *.srl *-temp.pem"
Step 5: Generate the Wazuh indexer certificates
Wazuh components need certificates so the indexer, dashboard, manager, and related services can communicate securely. In a local lab, self-signed certificates are acceptable. In production, certificate handling needs real lifecycle ownership.
bash wazuh/certs/indexer_cluster/generate_certs.sh
PowerShell / WSL command
If you are running the lab from PowerShell with the repository at C:\k8s\wazuh-kubernetes, run the script through WSL:
wsl bash -lc "cd /mnt/c/k8s/wazuh-kubernetes && bash wazuh/certs/indexer_cluster/generate_certs.sh"
What you should see
The script should create certificate material for multiple Wazuh components. Successful output should show certificate and key creation rather than shell errors.
If this fails
- Confirm you are in the wazuh-kubernetes directory.
- Confirm the script exists at wazuh/certs/indexer_cluster/generate_certs.sh.
- Confirm your shell can run Bash scripts.
- Confirm file permissions allow the script to execute or run it explicitly through bash.
- If you see $'\r': command not found, convert the files to Linux line endings using the Windows / PowerShell step above.
Step 6: Generate the Wazuh dashboard certificates
The dashboard also needs certificate material for HTTPS access.
bash wazuh/certs/dashboard_http/generate_certs.sh
PowerShell / WSL command
If you are running the lab from PowerShell with the repository at C:\k8s\wazuh-kubernetes, run the script through WSL:
wsl bash -lc "cd /mnt/c/k8s/wazuh-kubernetes && bash wazuh/certs/dashboard_http/generate_certs.sh"
What this means
The generated certificate files are later consumed by Kustomize secret generators or secret manifests so Kubernetes can mount the certificate material into the appropriate Wazuh components.
Step 7: Confirm your storage class
Wazuh has stateful components, so storage must work before the platform will become healthy.
kubectl get sc
Look for a default StorageClass. If your cluster uses a local lab platform, the provisioner name may be different than Wazuh's examples. That is normal. What matters is that your PVCs can bind successfully.
If you need to adjust storage
Wazuh's local deployment includes storage configuration under envs/local-env/. Review the storage class file before applying manifests:
cat envs/local-env/storage-class.yaml
If your lab requires a different provisioner, adjust the file before applying the local manifests. Do not guess. Match the provisioner to what your cluster actually supports.
Step 8: Review the local deployment overlay
Before you deploy, inspect the local overlay.
cat envs/local-env/kustomization.yml kubectl kustomize envs/local-env/ | head -n 80
What you are checking
- The namespace and resources being created
- Secret generators or secret references
- Services and service types
- Stateful components and PVC definitions
- Image names and tags
- Resource requests, limits, and storage sizes
Step 9: Apply the local Kubernetes manifests
Before applying the manifests, confirm the generated certificate files exist. Kustomize will fail if files such as wazuh/certs/indexer_cluster/root-ca.pem are missing.
Get-ChildItem .\wazuh\certs\indexer_cluster\*.pem Get-ChildItem .\wazuh\certs\dashboard_http\*.pem
You should see files such as:
root-ca.pem node.pem node-key.pem dashboard.pem dashboard-key.pem admin.pem admin-key.pem filebeat.pem filebeat-key.pem
For a local lab, use the local-env overlay.
kubectl apply -k envs/local-env/
Expected warning if the namespace already exists
If you created the wazuh namespace earlier during lab preparation, you may see a warning similar to this:
Warning: resource namespaces/wazuh is missing the kubectl.kubernetes.io/last-applied-configuration annotation... namespace/wazuh configured
This is not a deployment failure. It means the namespace was originally created with kubectl create namespace instead of kubectl apply, so it did not have the apply-tracking annotation. Kubernetes patches the annotation automatically and continues.
What this creates
Expect Kubernetes to create the Wazuh namespace, secrets, services, StatefulSets, Deployments, persistent volume claims, and supporting configuration.
Step 10: Watch the deployment come up
Stateful platforms take time. Watch the pods instead of immediately assuming failure.
kubectl get pods -n wazuh -w
Press Ctrl+C when you are done watching.
What you may see
- Pending: scheduling or storage may not be ready.
- ContainerCreating: images, volumes, or mounts are being prepared.
- Running: the container started.
- Ready 0/1: the container started but readiness checks may not be satisfied yet.
- CrashLoopBackOff: the container started and failed repeatedly.
Step 11: Verify the Wazuh namespace
kubectl get namespaces | grep wazuh
You should see the wazuh namespace active.
Step 12: Verify Wazuh services
kubectl get services -n wazuh kubectl get services -n wazuh -o wide
Look for services for the dashboard, indexer, Wazuh manager, Wazuh cluster, and workers. Exact names and service types can vary by version and environment.
Step 13: Verify pods, storage, and events
kubectl get pods -n wazuh -o wide kubectl get statefulset -n wazuh kubectl get deployment -n wazuh kubectl get pvc -n wazuh kubectl get secrets -n wazuh kubectl get events -n wazuh --sort-by=.lastTimestamp
What healthy looks like
- Pods should eventually reach Running and Ready states.
- PVCs should be Bound.
- Services should exist for expected components.
- Events should not show ongoing image pull, mount, scheduling, or readiness failures.
Step 14: Access the dashboard with port-forwarding
In a local lab, do not expose the dashboard broadly. Use port-forwarding to bind dashboard access to the local machine.
kubectl -n wazuh port-forward --address 127.0.0.1 service/dashboard 8443:443
Then open:
https://127.0.0.1:8443
Your browser may warn about the certificate because the lab uses self-signed certificates.
Step 15: Login and immediately review credentials
Wazuh's Kubernetes deployment documentation lists the default lab dashboard credentials as:
Username: admin Password: SecretPassword
Use these only as lab defaults. After confirming the dashboard works, review the Wazuh documentation for changing default user passwords and document what you changed.
Step 16: Document the deployment
Before moving to the next lesson, capture a short deployment record.
Deployment date: Wazuh branch/version: Kubernetes context: StorageClass: Namespace: Dashboard access method: Default credentials changed: Pods healthy: PVCs bound: Windows / PowerShell line-ending fix needed: Known issues: Cleanup procedure:
Common deployment problems
Certificate script fails with $'\r': command not found
This means the Bash scripts have Windows CRLF line endings. Convert the certificate scripts and config files with dos2unix, remove partial certificate output, and rerun both certificate generation scripts through WSL.
wsl bash -lc "cd /mnt/c/k8s/wazuh-kubernetes && find wazuh/certs -type f \( -name '*.sh' -o -name '*.cnf' -o -name '*.conf' -o -name '*.yml' -o -name '*.yaml' \) -print0 | xargs -0 dos2unix" wsl bash -lc "cd /mnt/c/k8s/wazuh-kubernetes/wazuh/certs/indexer_cluster && rm -f *.pem *.csr *.srl *-temp.pem && cd ../dashboard_http && rm -f *.pem *.csr *.srl *-temp.pem" wsl bash -lc "cd /mnt/c/k8s/wazuh-kubernetes && bash wazuh/certs/indexer_cluster/generate_certs.sh && bash wazuh/certs/dashboard_http/generate_certs.sh"
Kustomize fails because root-ca.pem is missing
This means certificate generation did not complete successfully. Confirm the PEM files exist before applying manifests.
Get-ChildItem .\wazuh\certs\indexer_cluster\*.pem Get-ChildItem .\wazuh\certs\dashboard_http\*.pem
kubectl apply warns that the wazuh namespace is missing the last-applied annotation
This is expected if you created the namespace earlier with kubectl create namespace wazuh. The apply command patches the missing annotation automatically and continues. Treat it as a warning, not a deployment failure.
StatefulSet pods stay Pending because PVCs are Pending
If the dashboard is Running but the Wazuh indexer and manager pods are Pending, check PVCs before troubleshooting Wazuh itself.
kubectl get pvc -n wazuh kubectl get events -n wazuh --sort-by=.lastTimestamp
If events show failed to provision volume with StorageClass "wazuh-storage": configuration error, no node was specified, the local StorageClass likely needs volumeBindingMode: WaitForFirstConsumer. The next lesson walks through this in detail.
Pods stay Pending
kubectl describe pod -n wazuh <pod-name> kubectl get pvc -n wazuh kubectl describe pvc -n wazuh <pvc-name> kubectl get events -n wazuh --sort-by=.lastTimestamp
Most Pending problems are scheduling, resource, or storage related.
PVCs stay Pending
Check your StorageClass and provisioner. If dynamic provisioning is not working, Wazuh stateful components will not become healthy.
Image pull failures
kubectl describe pod -n wazuh <pod-name>
Look for image name, tag, registry, authentication, or network-related errors.
Readiness failures
kubectl logs -n wazuh <pod-name> kubectl describe pod -n wazuh <pod-name>
Readiness failures can happen while Wazuh components initialize. If they persist, inspect logs and events.
Dashboard does not open
- Confirm the dashboard service exists.
- Confirm the port-forward command is still running.
- Confirm you are browsing to https://127.0.0.1:8443.
- Confirm dashboard pods are Running and Ready.
Cleanup note
Do not delete Wazuh casually unless you are done with the lab. Stateful workloads can leave PVCs and storage behind. Use Wazuh cleanup guidance and confirm what will be removed before deleting resources.
Check your understanding
Use these questions to check whether the deployment concepts are clear before moving forward.
- Why should you confirm cluster health before applying Wazuh manifests?
- Why does the Wazuh version branch matter?
- What is the purpose of the Wazuh Kubernetes repository?
- Why are certificates generated before applying the manifests?
- Why does storage matter for Wazuh?
- What is the difference between envs/eks and envs/local-env?
- What does kubectl apply -k envs/local-env/ do?
- Why should you watch pods after applying manifests?
- What does a Bound PVC indicate?
- Why use port-forwarding for dashboard access in a local lab?
- Why are default credentials a security concern?
- What should you document after deployment?
- What should you check first if pods are Pending?
- What should you check if the dashboard will not open?
- What does $'\r': command not found usually mean when running the certificate scripts?
- Why should you remove partial certificate output after a failed certificate-generation attempt?
- Is the missing last-applied-configuration namespace warning a deployment failure?
Answer key
- A broken cluster makes Wazuh troubleshooting unreliable because platform failures can look like Wazuh failures.
- The branch controls which manifests, images, scripts, and deployment structure you are using.
- It provides the Kubernetes manifests, overlays, scripts, secrets, and configuration used to deploy Wazuh.
- Wazuh components use certificate material for secure component communication and dashboard access.
- Stateful Wazuh components need persistent storage, and pods may not start if PVCs cannot bind.
- envs/eks is for Amazon EKS; envs/local-env is for other/local Kubernetes clusters.
- It applies the Kustomize local deployment overlay to the cluster.
- Stateful platforms take time, and watching status helps distinguish normal initialization from failure.
- The PVC has successfully matched to storage and is available for the pod.
- It avoids broadly exposing the sensitive dashboard while learning locally.
- They are publicly documented and should not remain in persistent, shared, or exposed deployments.
- Version, context, StorageClass, namespace, dashboard access, credential status, pod health, PVC state, known issues, and cleanup plan.
- Describe the pod, check PVCs, check resources, and review namespace events.
- Check the service name, port-forward process, URL, certificate warning, and dashboard pod readiness.
- The scripts have Windows CRLF line endings and need to be converted to Linux line endings before running through Bash.
- Partial files can leave the certificate directories in a confusing broken state, so clean them up before rerunning the scripts.
- No. In this lab it usually means the namespace was created earlier with kubectl create namespace; Kubernetes patches the annotation automatically and continues.