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 withnanoorvim. - (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.
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).
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).
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
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).
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:
sudo systemctl daemon-reload
sudo systemctl enable --now elasticsearch
Wait a moment, then test:
curl -s http://127.0.0.1:9200/_cluster/health?pretty
You should see "status": "green".
2.3 Install Logstash
sudo apt install -y logstash
We’ll create a simple pipeline later that ingests Wazuh alerts.
2.4 Install Kibana
sudo apt install -y kibana
Edit /etc/kibana/kibana.yml:
server.host: "127.0.0.1"
server.port: 5601
elasticsearch.hosts: ["http://127.0.0.1:9200"]
Enable and start Kibana:
sudo systemctl enable --now kibana
At this point you can reach Kibana via an SSH tunnel:
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:
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
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
sudo apt install -y wazuh-manager
Enable and start:
sudo systemctl enable --now wazuh-manager
Check status:
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:
<global>
<email_notification>no</email_notification>
</global>
Enable the API (under <wodle name="open-scap"> is not needed). Instead, uncomment or add:
<wodle name="wcst-restful">
<disabled>no</disabled>
<port>55000</port>
<host>127.0.0.1</host>
</wodle>
Restart the manager:
sudo systemctl restart wazuh-manager
Test the API:
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
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.
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:
sudo systemctl enable --now wazuh-agent
Verify connection:
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.
sudo /usr/share/logstash/bin/logstash-plugin install logstash-input-wazuh
5.2 Create a Logstash pipeline
Create /etc/logstash/conf.d/wazuh.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
elasticuser (sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic) and put it in thepasswordfield.
Test the pipeline:
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:
sudo systemctl enable --now logstash
Check logs:
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.
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):
./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
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:
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
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:
sudo ocsinventory-agent
Check the OCS web UI under Computers → List 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:
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.
7.2 Enable Elasticsearch Security (optional but recommended)
If you plan to expose any ports publicly, enable basic auth and TLS.
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:
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:
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:
sudo crontab -e
Add:
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:
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).
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 Computers → List and confirm your VPS appears with hardware details (CPU, RAM, installed packages).
8.3 Check Logstash Pipeline
Run:
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!