Why deploy Wazuh on your VPS
A Linux server constantly emits security signals: SSH login attempts, system file changes, privilege escalations, unusual processes. These events live in scattered logs — /var/log/auth.log, your container journals, application logs — that nobody correlates. A SIEM (Security Information and Event Management) is precisely the tool that centralises these streams, analyses them and surfaces incidents.
Wazuh is today the open source reference in this space. Its architecture relies on a manager (the detection brain) that receives data from lightweight agents installed on each of your servers. The manager embeds a rule correlation engine, a File Integrity Monitoring (FIM) module, a vulnerability detection module and out-of-the-box regulatory compliance dashboards.
Unlike Datadog SIEM or cloud-hosted Elastic SIEM, Wazuh does not charge per ingestion and your data stays within your infrastructure. This is the approach to adopt once you manage several servers and need to justify your security posture — for an audit, for a client, or simply to know what is actually happening.
What Wazuh brings to your infrastructure
- Centralised logs from all your Linux servers in a single dashboard: no more SSH machine-hopping to read
auth.log. - Rule-based intrusion detection: brute-force attempts, privilege escalations, critical file modifications — with multi-source correlation.
- Real-time File Integrity Monitoring (FIM): any modification to
/etc/passwd,/etc/sudoersor your system binaries triggers an immediate alert. - PCI-DSS, HIPAA, GDPR and NIST SP 800-53 compliance rules built into the default ruleset — every alert is automatically tagged with the regulatory controls it touches.
- Active vulnerability detection: Wazuh queries CVE databases and identifies installed packages exposed to known CVEs.
- Complementary to CrowdSec and Fail2ban: Wazuh correlates and archives for compliance, CrowdSec acts at the perimeter — the two tools reinforce each other without duplicating.
- No ingestion cost: you keep history as long as your disk allows.
Minimum requirements before you start
The Wazuh single-node deployment (manager + indexer + dashboard in three Docker containers) is more resource-intensive than a simple monitoring agent. Here are the realistic minimums for a production use case.
For the manager (central node):
- 4 vCPU minimum, 8 vCPU recommended for a fleet of 10 servers or more.
- 8 GB of RAM minimum for the single-node (manager + indexer + dashboard). Plan for 16 GB if you index a high volume of events or manage more than 50 agents.
- 50 GB SSD disk minimum; 100 GB or more if you retain 90 days of history across multiple servers.
- Ubuntu 22.04 LTS or Debian 12, kept up to date.
- Docker Engine 24.0+ and Docker Compose v2 installed.
- A domain name to expose the Wazuh dashboard over HTTPS via a reverse proxy.
For each agent (on your other servers):
- The Wazuh agent is very lightweight: under 64 MB of RAM and under 1% CPU under normal load.
- Compatible with Linux (Debian, Ubuntu, AlmaLinux, Rocky), Windows and macOS.
Network ports to open on the manager:
- 1514/UDP and 1514/TCP: agent → manager communication.
- 1515/TCP: agent registration.
- 55000/TCP: Wazuh REST API (local access only, do not expose publicly).
- 9200/TCP and 9300/TCP: Wazuh Indexer (internal use between containers).
- 443/TCP: Wazuh Dashboard via your reverse proxy.
Deploy Wazuh with Docker Compose: full procedure
Prepare the server and install Docker
Update the system, then install Docker Engine and Docker Compose v2 from the official Docker repository:
apt-get update && apt-get upgrade -y curl -fsSL https://get.docker.com | sh docker --version && docker compose versionVerify that Docker Compose v2 responds correctly (command
docker compose, without a hyphen). Enable Docker at boot:systemctl enable --now docker.Clone the official Wazuh Docker repository
Wazuh publishes its official Docker Compose files in the
wazuh/wazuh-dockerrepository. Clone the branch matching the current stable version (v4.14.x at the time of this article):git clone https://github.com/wazuh/wazuh-docker.git -b v4.14.8 --depth 1 cd wazuh-docker/single-nodeThe
single-nodefolder contains the pre-wireddocker-compose.ymlwith three services:wazuh.manager,wazuh.indexerandwazuh.dashboard.Generate inter-component TLS certificates
Wazuh requires TLS certificates for communication between the manager, indexer and dashboard. The repository provides a dedicated Compose file for their generation:
docker compose -f generate-indexer-certs.yml run --rm generatorCertificates are written to
config/wazuh_indexer_ssl_certs/. This step is required only once; to renew, re-run the command and then restart the stack.Start the Wazuh stack
Launch the three containers in the background:
docker compose up -dThe first start downloads the official images (approximately 2 GB total) and initialises the indexer. Wait 60 to 90 seconds, then check that all three services are
healthy:docker compose psIf
wazuh.indexerstays instartingafter 3 minutes, inspect its logs:docker compose logs wazuh.indexer | tail -50.Change the default dashboard password
The default Wazuh dashboard credentials are
admin/SecretPassword. Change them immediately via the OpenSearch indexer API:docker compose exec wazuh.indexer curl -sk -X PUT \ https://localhost:9200/_plugins/_security/api/account \ -u admin:SecretPassword \ -H 'Content-Type: application/json' \ -d '{"password": "YourNewPassword", "current_password": "SecretPassword"}'Then update the
DASHBOARD_PASSWORDvariable indocker-compose.ymland re-rundocker compose up -dso the dashboard uses the new password.Expose the dashboard via an HTTPS reverse proxy
Do not expose the dashboard directly on port 443 without a reverse proxy. With nginx, create a vhost that proxies to
https://127.0.0.1:5601(the internal dashboard port) while disabling upstream self-signed certificate verification:server { listen 443 ssl; server_name wazuh.your-domain.com; location / { proxy_pass https://127.0.0.1:5601; proxy_ssl_verify off; } }Renew your Let's Encrypt certificate with Certbot (
certbot --nginx -d wazuh.your-domain.com). Do not expose ports 9200, 55000 or 1514/1515 publicly — ports 1514-1515 should only be open to your own servers.Install and register an agent on a remote server
On each Linux server you want to monitor, install the Wazuh agent pointing to your manager's IP or DNS name. On Ubuntu/Debian:
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 && apt-get install -y wazuh-agentSet the manager address in
/var/ossec/etc/ossec.conf(tag<address>), then start and enable the agent:systemctl daemon-reload systemctl enable wazuh-agent systemctl start wazuh-agentThe agent appears in the dashboard under Agents within 30 seconds.
Post-installation configuration: agents, rules and compliance dashboards
Once the stack is running and your first agents are connected, three adjustments significantly improve alert relevance.
Enable File Integrity Monitoring (FIM). In the agent configuration (/var/ossec/etc/ossec.conf), add sensitive directories to the <syscheck> section:
<syscheck>
<frequency>43200</frequency>
<directories check_all="yes">/etc,/usr/bin,/usr/sbin</directories>
<directories check_all="yes">/var/www</directories>
</syscheck>Every modification in these directories triggers an alert of level 7 or higher.
Explore the compliance dashboards. In the Wazuh dashboard, the Modules section exposes pre-built views for PCI-DSS, HIPAA, GDPR and NIST SP 800-53. Each alert automatically inherits the regulatory tags defined in the manager's XML rules — for example, a privilege escalation attempt simultaneously tags PCI-DSS 10.2.5 and HIPAA 164.312(b).
Adjust the alert level threshold. The default threshold is 3. To receive only significant alerts, edit /var/ossec/etc/ossec.conf on the manager and raise the threshold to 7 in the <alerts> section:
<alerts>
<log_alert_level>7</log_alert_level>
<email_alert_level>9</email_alert_level>
</alerts>Restart the manager after any configuration change: docker compose restart wazuh.manager.
Hardening your deployment
A few settings reduce the attack surface of the manager itself.
Isolate the manager on its own VPS or, at minimum, behind a firewall that only opens ports 1514-1515 to your agents' IPs — never to 0.0.0.0. On a ServOrbit VPS, the VPS administration option gives you access to pre-configured ufw: ufw allow from <AGENT_IP> to any port 1514 proto tcp.
Enable mutual TLS authentication between agents and manager by generating agent certificates signed by your internal CA rather than using automatic registration (ossec-authd). The official Wazuh documentation details the procedure under "Agent enrollment via the Wazuh manager API".
Encrypt the Docker volumes that contain the OpenSearch index and configurations — especially if you host the manager on a shared VPS. A dump of the indexer contains your entire security log history.
Monitor the manager itself: install a Wazuh agent on the VPS running the manager to detect any modification of Docker images or configuration files.
Troubleshooting: common errors
Here are the five most common issues when deploying Wazuh with Docker, with exact messages and fixes.
1. max virtual memory areas vm.max_map_count [65530] is too low
The OpenSearch indexer requires at least 262144. Add this line to /etc/sysctl.conf on the host (not inside the container): vm.max_map_count=262144, then apply with sysctl -p. This is the most common error on a fresh VPS.
2. wazuh.indexer stays in starting state indefinitely
Check the logs first (docker compose logs wazuh.indexer). If you see bootstrap checks failed, it is almost always vm.max_map_count (see above) or insufficient RAM. If you see certificate not found, the certificates were not generated correctly — re-run docker compose -f generate-indexer-certs.yml run --rm generator.
3. Agent shows Disconnected in the dashboard
Verify that port 1514 is open inbound on the manager and that the manager address is correctly set in /var/ossec/etc/ossec.conf on the agent. Test connectivity from the agent: nc -zv <MANAGER_IP> 1514. If the connection fails, check your firewall (ufw status, CSF rules).
4. ERROR: [agent_auth] Unable to create ssl context
The agent cannot validate the manager's TLS certificate. Check that ossec.cfg on the agent points to the DNS name rather than the bare IP if you are using a named certificate. Alternatively, disable certificate verification on the agent side during testing: use the tag <verify_host>no</verify_host> in the registration configuration (do not leave this in production).
5. Too many open files in manager logs
Increase nofile limits on the host. In /etc/security/limits.conf: * soft nofile 65536 and * hard nofile 65536. In docker-compose.yml, add ulimits: nofile: soft: 65536 hard: 65536 to the wazuh.manager service, then restart.
Wazuh in your security stack
Wazuh is most effective when integrated with what you already have. Combined with CrowdSec (which blocks IPs at the network level), it covers both the perimeter and deep system-level monitoring. Combined with Fail2ban, it adds correlation and regulatory archiving that Fail2ban cannot produce on its own. And if you already run a Grafana + Prometheus stack, Wazuh complements system observability with a security layer: Prometheus tells you CPU is at 90%, Wazuh tells you why — and whether it is a cryptomining attempt.
Hardening your server (Linux VPS hardening checklist) defines a static security posture. Wazuh is the continuous monitoring that verifies that posture holds over time — that nobody has modified /etc/sudoers, that your system binaries have not been replaced, that authentication events remain within normal ranges.
To host your Wazuh manager on infrastructure whose security level you control, see how ServOrbit structures its infrastructure security and choose the VPS that matches your agent fleet.