Mahbubur Riad
Back to blog
DevOps 8 min read

Self-Hosted SIEM on a $5 VPS: Step‑By‑Step Guide to Deploying Wazuh with OCS Inventory and ELK Stack for Intrusion Detection

Jul 03, 2026 · Mahbubur Riad

Learn how to build a low-cost SIEM using Wazuh, OCS Inventory, and the ELK stack on a $5 VPS. Step-by-step install, configuration, hardening, and practical tips for real‑world intrusion detection.

On this page

Self-Hosted SIEM on a $5 VPS: Step‑By‑Step Guide to Deploying Wazuh with OCS Inventory and ELK Stack for Intrusion Detection

Running a Security Information and Event Management (SIEM) system used to mean buying expensive hardware or subscribing to costly SaaS platforms. Today, with powerful open‑source tools and ultra‑cheap cloud VPS offerings, you can assemble a capable SIEM for under $5 per month. This tutorial walks you through installing Wazuh (the agent‑based log collector and rule engine), pairing it with the ELK stack (Elasticsearch, Logstash, Kibana) for storage and visualization, and adding OCS Inventory for asset discovery. By the end you’ll have a centralized log pipeline, real‑time alerting, and a basic asset inventory—all running on a single $5 VPS.

Honest note: This setup is not a replacement for an enterprise‑grade SIEM with dedicated correlation engines and high‑availability clusters. It’s designed for learning, small‑to‑medium environments, and as a solid foundation you can later scale out.

Prerequisites

  • A VPS with at least 1 GB RAM, 25 GB SSD, and 1 vCPU (many providers offer this for $5/mo). Ubuntu 22.04 LTS is used here.
  • Root or sudo access.
  • Basic familiarity with Linux CLI, systemctl, and editing files with nano or vim.
  • (Optional) A domain name pointing to the VPS for HTTPS on Kibana—if you don’t have one, you can still access Kibana via HTTP on localhost and tunnel with SSH.

1. Prepare the VPS

First, update the OS and install essential packages.

Bash
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl gnupg2 apt-transport-https ca-certificates software-properties-common

Create a non‑root user for daily operations (replace sammy with your preferred name).

Bash
sudo adduser sammy
sudo usermod -aG sudo sammy

Log out and back in as sammy, or use su - sammy.

Set the hostname (helps with log identification).

Bash
sudo hostnamectl set-hostname siem-vps

2. Install the ELK Stack

We’ll install Elasticsearch 8.x, Logstash 8.x, and Kibana 8.x from the official Elastic APT repositories.

2.1 Add Elastic repository and install Elasticsearch

Bash
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elastic-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/elastic-archive-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
sudo apt update
sudo apt install -y elasticsearch

2.2 Configure Elasticsearch for single‑node

Edit /etc/elasticsearch/elasticsearch.yml (use sudo nano).

YAML
cluster.name: siem-cluster
node.name: node-1
network.host: 127.0.0.1   # bind to localhost for security
http.port: 9200
discovery.type: single-node
xpack.security.enabled: false   # we’ll enable basic auth later via nginx or rely on firewall

Start and enable Elasticsearch:

Bash
sudo systemctl daemon-reload
sudo systemctl enable --now elasticsearch

Wait a moment, then test:

Bash
curl -s http://127.0.0.1:9200/_cluster/health?pretty

You should see "status": "green".

2.3 Install Logstash

Bash
sudo apt install -y logstash

We’ll create a simple pipeline later that ingests Wazuh alerts.

2.4 Install Kibana

Bash
sudo apt install -y kibana

Edit /etc/kibana/kibana.yml:

YAML
server.host: "127.0.0.1"
server.port: 5601
elasticsearch.hosts: ["http://127.0.0.1:9200"]

Enable and start Kibana:

Bash
sudo systemctl enable --now kibana

At this point you can reach Kibana via an SSH tunnel:

Bash
ssh -L 5601:127.0.0.1:5601 sammy@your_vps_ip

Then open http://localhost:5601 in your browser.

2.5 Secure the stack (basic)

Because we bound Elasticsearch and Kibana to localhost, only local processes can talk to them. We’ll expose Kibana through Nginx with basic auth and HTTPS (optional). For brevity, we’ll just use SSH tunneling for admin access and rely on the firewall for inbound blocks.

Install ufw and allow only SSH:

Bash
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw enable

3. Install Wazuh Manager

Wazuh consists of a manager (server) and agents. We’ll install the manager on the same VPS.

3.1 Add Wazuh repository

Bash
curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | sudo apt-key add-s https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-repo/wazuh-repo_4.5.0_all.deb -O /tmp/wazuh-repo.deb
sudo dpkg -i /tmp/wazuh-repo.deb
sudo apt update

3.2 Install the manager

Bash
sudo apt install -y wazuh-manager

Enable and start:

Bash
sudo systemctl enable --now wazuh-manager

Check status:

Bash
sudo systemctl status wazuh-manager

3.3 Basic manager configuration

The manager reads its configuration from /var/ossec/etc/ossec.conf. We’ll enable the built‑in RESTful API to talk to Logstash later.

Edit /var/ossec/etc/ossec.conf and locate the <client><server> block (if you plan to add agents later). For now, ensure the <global> section has:

XML
<global>
  <email_notification>no</email_notification>
</global>

Enable the API (under <wodle name="open-scap"> is not needed). Instead, uncomment or add:

XML
<wodle name="wcst-restful">
  <disabled>no</disabled>
  <port>55000</port>
  <host>127.0.0.1</host>
</wodle>

Restart the manager:

Bash
sudo systemctl restart wazuh-manager

Test the API:

Bash
curl -k -u wazuh:wazuh https://127.0.0.1:55000/security/user/auth

You should get a JSON token (default credentials are wazuh:wazuh; change them later).

3.4 Change default API credentials

Bash
sudo /var/ossec/api/scripts/manage_auth_users.exe -a -p wazuh -r

Follow the prompts to set a new password.

4. Install and Configure Wazuh Agent (local)

Even though the manager is on the same host, installing the agent lets you monitor the VPS itself.

Bash
sudo apt install -y wazuh-agent

During installation, when prompted for the manager’s IP, enter 127.0.0.1. Choose the default protocol (TCP) and port 1514.

Enable and start the agent:

Bash
sudo systemctl enable --now wazuh-agent

Verify connection:

Bash
sudo /var/ossec/bin/agent_control -l

You should see the agent listed as Active.

5. Connect Wazuh to ELK via Logstash

We’ll configure Logstash to pull alerts from the Wazuh API and push them into Elasticsearch.

5.1 Install the Wazuh Logstash plugin

Logstash ships with a plugin for Wazuh; we just need to enable it.

Bash
sudo /usr/share/logstash/bin/logstash-plugin install logstash-input-wazuh

5.2 Create a Logstash pipeline

Create /etc/logstash/conf.d/wazuh.conf:

CONF
input {
  wazuh {
    api_user => "wazuh"
    api_password => "YOUR_NEW_PASSWORD"   # replace with the password you set
    api_host => "127.0.0.1"
    api_port => 55000
    interval => 60
    verify_ssl => false
  }
}

filter {
  # Optionally add geoip enrichment or mutate fields
  if [@metadata][type] == "alert" {
    mutate {
      add_field => { "source_ip" => "%{[data][srcip]}" }
    }
  }
}

output {
  elasticsearch {
    hosts => ["http://127.0.0.1:9200"]
    index => "wazuh-alerts-%{+YYYY.MM.dd}"
    user => "elastic"
    password => ""   # if you enabled security, set credentials here
  }
  stdout { codec => rubydebug }
}

Note: If you enabled Elasticsearch security, generate a password for the elastic user (sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic) and put it in the password field.

Test the pipeline:

Bash
sudo /usr/share/logstash/bin/logstash --path.settings /etc/logstash -f /etc/logstash/conf.d/wazuh.conf --debug

You should see events flowing into Elasticsearch. Press Ctrl+C to stop.

Enable and start Logstash as a service:

Bash
sudo systemctl enable --now logstash

Check logs:

Bash
sudo journalctl -u logstash -f

5.3 Load the Wazuh dashboard into Kibana

Wazuh provides a set of Kibana objects (visualizations, dashboards, index patterns). Download and import them.

Bash
curl -s https://packages.wazuh.com/4.x/dashboard/wazuh-dashboard-4.5.0.tar.gz | tar -xz
cd wazuh-dashboard-4.5.0

Run the helper script (it will ask for Elasticsearch URL and credentials):

Bash
./install_dashboard.sh

When prompted:

  • Elasticsearch URL: http://127.0.0.1:9200
  • Username: elastic
  • Password: (the one you set or leave blank if disabled)
  • Kibana URL: http://127.0.0.1:5601

The script will create the index pattern wazuh-alerts-* and import dashboards.

Now, open Kibana (via SSH tunnel) and navigate to Dashboard[Wazuh] Overview. You should see panels showing alerts, agents, and rule groups.

6. Install OCS Inventory Agent for Asset Discovery

OCS Inventory collects hardware/software data from Linux and Windows hosts. We’ll install the server side on the same VPS (lightweight) and the agent on the VPS itself.

6.1 Install OCS Inventory Server

Bash
sudo apt install -y ocsinventory-server

During installation, choose Apache as the web server and let it configure the database (it will use MySQL/MariaDB). Set a password for the OCS admin user when prompted.

After install, enable the site:

Bash
sudo a2ensite ocsinventory-reports
sudo a2enmod rewrite
sudo systemctl restart apache2

6.2 Configure OCS Inventory to talk to ELK (optional)

If you want OCS data also in Kibana, you can configure Logstash to read the MySQL database. For simplicity, we’ll just use the OCS web UI for asset inventory and keep logs separate.

Access the OCS GUI: http://your_vps_ip/ocsinventory. Log in with the admin credentials you set.

6.3 Install OCS Inventory Agent on the VPS

Bash
sudo apt install -y ocsinventory-agent

When asked for the server address, enter http://127.0.0.1/ocsinventory. Choose to run via cron (default). The agent will now send inventory data to the OCS server every 24 hours.

You can force an immediate run:

Bash
sudo ocsinventory-agent

Check the OCS web UI under ComputersList to see your VPS appear.

7. Hardening the SIEM Stack

Even on a $5 VPS, basic hardening reduces the attack surface.

7.1 Firewall Rules

Allow only needed ports:

  • SSH (22) – already allowed.
  • Elasticsearch (9200) – blocked to localhost only (bind to 127.0.0.1).
  • Logstash (5044) – if you plan to send beats from other hosts, open it; otherwise keep localhost.
  • Wazuh API (55000) – localhost only.
  • OCS inventory (80) – you may want to restrict to your IP or use HTTPS.

Example UFW rules:

Bash
sudo ufw allow from 192.0.2.0/24 to any port 9200 proto tcp comment 'Allow ES from trusted net'
sudo ufw allow from 192.0.2.0/24 to any port 5044 proto tcp comment 'Allow Logstash beats'
sudo ufw allow from 192.0.2.0/24 to any port 55000 proto tcp comment 'Allow Wazuh API'
sudo ufw allow from 192.0.2.0/24 to any port 80 proto tcp comment 'Allow OCS web'
sudo ufw enable

Replace 192.0.2.0/24 with your management network or your own IP.

If you plan to expose any ports publicly, enable basic auth and TLS.

Bash
sudo /usr/share/elasticsearch/bin/elasticsearch-certutil ca
sudo /usr/share/elasticsearch/bin/elasticsearch-certutil cert --ca elastic-stack-ca.p12

Then edit elasticsearch.yml:

YAML
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: elastic-certificates.p12
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: elastic-certificates.p12
xpack.security.http.ssl.truststore.path: elastic-certificates.p12

Set passwords for built‑in users:

Bash
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u kibana

Update Logstash and Kibana configs with the appropriate user/password and ssl settings.

7.3 Regular Updates

Create a simple cron job to update packages weekly:

Bash
sudo crontab -e

Add:

Text
0 3 * * 0 apt update && apt upgrade -y >> /var/log/auto-update.log 2>&1

7.4 Log Rotation

Ensure /var/log/wazuh and /var/log/ocsinventory are rotated. The packages usually install logrotate configs; verify:

Bash
ls /etc/logrotate.d/wazuh
ls /etc/logrotate.d/ocsinventory-agent

8. Testing Your SIEM

8.1 Generate a Test Alert

Trigger a rule by creating a file that matches a Wazuh rule (e.g., a suspicious SSH login attempt).

Bash
sudo touch /etc/passwd.bak
sudo chmod 000 /etc/passwd.bak

Wazuh’s syscheck will detect the permission change and fire an alert. Wait a minute, then check Kibana:

  • Go to Discover → index pattern wazuh-alerts-*.
  • You should see an alert with rule ID 550 (file permission changed).

8.2 Verify OCS Inventory

In the OCS web UI, navigate to ComputersList and confirm your VPS appears with hardware details (CPU, RAM, installed packages).

8.3 Check Logstash Pipeline

Run:

Bash
curl -s http://127.0.0.1:9200/wazuh-alerts-*/_count?pretty

You should see a non‑zero count.

9. Maintenance Checklist

Task Frequency Command / Action
Update OS packages Weekly apt update && apt upgrade -y
Update Wazuh manager/agent Monthly apt install --only-upgrade wazuh-manager wazuh-agent
Update Elasticsearch/Kibana/Logstash Monthly Same as above via Elastic repo
Check disk usage Weekly df -h and du -sh /var/lib/elasticsearch
Review Wazuh rules Monthly /var/ossec/etc/rules/local_rules.xml
Backup Elasticsearch snapshots Daily (if data critical) Use Elasticsearch Snapshot API
Rotate OCS inventory logs Daily (handled by logrotate) Verify /etc/logrotate.d/ocsinventory-agent
Test alert generation Monthly Create a test file as in section 8.1

10. Scaling Beyond the Single VPS

When you outgrow the $5 box, consider:

  • Splitting Elasticsearch onto its own dedicated node (minimum 2 GB RAM).
  • Using a managed Redis or RabbitMQ as a broker between Logstash beats and Elasticsearch.
  • Adding Filebeat or Metricbeat agents on remote servers to ship logs directly to Logstash.
  • Enabling multi‑node Wazuh manager cluster for higher event throughput.
  • Using a reverse proxy (NGINX) with Let’s Encrypt to expose Kibana securely.

Even with these additions, the core architecture remains the same: agents → Wazuh manager → Logstash → Elasticsearch → Kibana, with OCS Inventory running alongside for asset tracking.

11. Conclusion

You now have a fully functional, open‑source SIEM running on a budget VPS, capable of collecting logs, generating actionable alerts, and providing an asset inventory via OCS Inventory. The stack is lightweight enough for a $5/mo server yet powerful enough to grow into a larger deployment.

If you found this guide useful, explore more security and DevOps-focused writingahuburriad.com.

Happy monitoring, and stay secure!

Related

Related posts