You built the homelab. Proxmox is humming, Docker containers are stacked, Tailscale connects everything. Then something breaks at 2 AM, and you spend an hour SSH-ing into boxes trying to figure out which service died and why.
That's the gap between running infrastructure and actually observing it. Monitoring turns your homelab from a collection of machines into a system you can reason about. You see failed logins, disk pressure, container restarts, and network anomalies in one place instead of grepping through logs on six different hosts.
Splunk is the industry standard for log aggregation and analysis. The enterprise version costs more than most homelabs are worth, but Splunk Free gives you 500 MB of daily ingestion with no time limit. For a homelab — even a busy one — that's plenty. You get the same search engine, the same dashboarding, the same SPL query language that Fortune 500 SOC teams use. The only things you lose are alerting, authentication, and distributed search. We'll work around those.
This guide walks through the full setup: installing Splunk on Ubuntu, ingesting logs from Proxmox, Docker, and system services, building dashboards that surface what matters, and writing SPL queries that turn raw log data into answers.
Hardware requirements: Splunk Free runs comfortably on 2 CPU cores and 4 GB of RAM. A dedicated VM or container is ideal — don't run it on the same box as your production workloads. Budget 1 GB of disk per day of retention at typical homelab ingestion rates.
01 Installing Splunk Free on Ubuntu
Splunk distributes Linux packages directly. Skip the package manager — you want the official .deb from splunk.com so you control the version.
Download and install
Create a free Splunk account at splunk.com/download if you don't have one. Grab the latest .deb for Linux x86_64. At the time of writing, that's Splunk 9.3.x.
# Download (replace with current version URL from splunk.com)
wget -O splunk.deb 'https://download.splunk.com/products/splunk/releases/9.3.1/linux/splunk-9.3.1-0b8d769cb912-linux-amd64.deb'
# Install
sudo dpkg -i splunk.deb
# Accept license and set admin credentials
sudo /opt/splunk/bin/splunk start --accept-license --answer-yes --seed-passwd 'YourStrongPassword'
# Enable boot start
sudo /opt/splunk/bin/splunk enable boot-start -systemd-managed 1
Splunk is now running on port 8000. Open http://your-server-ip:8000 in a browser and log in with the admin credentials you just set.
Switch to the Free license
By default, Splunk starts with a 60-day Enterprise trial. Switch to the Free license immediately — you don't want features disappearing mid-setup when the trial expires.
# In the web UI:
# Settings → Licensing → Change license group → Free license → Save
# Or via CLI:
sudo /opt/splunk/bin/splunk edit licenser-groups Free -is_active 1
sudo /opt/splunk/bin/splunk restart
The Free license removes authentication (anyone with network access can reach the UI), scheduled searches, and alerting. For a homelab on a private network or behind Tailscale, the auth limitation doesn't matter. We'll address alerting later.
Lock down network access
Since Free license has no built-in auth, firewall it so only your local network or Tailscale subnet can reach the web UI and receiving ports:
# Allow only Tailscale subnet
sudo ufw allow from 100.64.0.0/10 to any port 8000 proto tcp
sudo ufw allow from 100.64.0.0/10 to any port 8089 proto tcp
sudo ufw allow from 100.64.0.0/10 to any port 9997 proto tcp
# Or restrict to your LAN
sudo ufw allow from 192.168.1.0/24 to any port 8000 proto tcp
02 Configuring Data Inputs
Splunk is useless without data. The three main ways to get logs in: monitor local files, receive syslog over the network, and deploy Universal Forwarders on remote hosts. For a homelab, we'll use all three.
Monitor local system logs
If Splunk runs on a host that also generates interesting logs (auth, syslog, kernel), monitor them directly:
# Add via CLI
sudo /opt/splunk/bin/splunk add monitor /var/log/syslog -index main -sourcetype syslog
sudo /opt/splunk/bin/splunk add monitor /var/log/auth.log -index main -sourcetype linux_secure
# Or create /opt/splunk/etc/system/local/inputs.conf:
[monitor:///var/log/syslog]
disabled = false
index = main
sourcetype = syslog
[monitor:///var/log/auth.log]
disabled = false
index = main
sourcetype = linux_secure
Receive syslog from network devices
Proxmox nodes, routers, and switches can forward syslog to Splunk over UDP or TCP. Enable a syslog listener:
# In /opt/splunk/etc/system/local/inputs.conf:
[udp://514]
disabled = false
connection_host = ip
sourcetype = syslog
index = main
[tcp://1514]
disabled = false
connection_host = ip
sourcetype = syslog
index = main
Use TCP port 1514 instead of UDP 514 when possible — TCP guarantees delivery and doesn't silently drop messages under load. UDP 514 is there for devices that only support the traditional syslog protocol.
Forward from Proxmox
On each Proxmox node, add a syslog forwarding rule. This sends all system logs to your Splunk instance:
# On the Proxmox node, create /etc/rsyslog.d/50-splunk.conf:
*.* @@splunk-host:1514
# Restart rsyslog
systemctl restart rsyslog
The @@ prefix means TCP. A single @ would mean UDP. Point splunk-host at the IP or Tailscale hostname of your Splunk box.
Ingest Docker container logs
Docker's default logging driver writes JSON to /var/lib/docker/containers/. Tell Splunk to monitor that path recursively:
# In inputs.conf:
[monitor:///var/lib/docker/containers/*/*-json.log]
disabled = false
index = docker
sourcetype = docker:json
Create the docker index first:
sudo /opt/splunk/bin/splunk add index docker
Each container's logs land with the container ID as the source. We'll parse out the container name in a later step.
Deploy Universal Forwarders
For remote hosts that don't support syslog forwarding (or where you want richer data), install Splunk Universal Forwarder. It's lightweight — around 100 MB of RAM — and sends logs to your Splunk instance over port 9997.
# On the remote host:
wget -O splunkforwarder.deb 'https://download.splunk.com/products/universalforwarder/releases/9.3.1/linux/splunkforwarder-9.3.1-0b8d769cb912-linux-amd64.deb'
sudo dpkg -i splunkforwarder.deb
# Configure and start
sudo /opt/splunkforwarder/bin/splunk start --accept-license --answer-yes --seed-passwd 'ForwarderPass'
sudo /opt/splunkforwarder/bin/splunk add forward-server splunk-host:9997
sudo /opt/splunkforwarder/bin/splunk add monitor /var/log/syslog -index main -sourcetype syslog
sudo /opt/splunkforwarder/bin/splunk enable boot-start -systemd-managed 1
On the Splunk server, enable the receiving port:
sudo /opt/splunk/bin/splunk enable listen 9997
Index strategy: At minimum, create separate indexes for
main(system logs),docker(container logs), andnetwork(firewall/router logs). Separate indexes let you set different retention policies and make searches faster by narrowing the scope.
03 Parsing and Field Extraction
Raw logs are searchable out of the box, but structured fields make everything faster. Splunk auto-extracts some fields (timestamp, host, source, sourcetype), but you'll want custom extractions for your specific log formats.
Docker container names
Docker JSON logs include the container ID in the file path but not the human-readable name. Add a transform to extract it:
# In /opt/splunk/etc/system/local/transforms.conf:
[docker_container_id]
REGEX = /var/lib/docker/containers/([a-f0-9]{12})
FORMAT = container_id::$1
DEST_KEY = MetaData:Host
For container names, use a scripted lookup or the Docker API. The simplest approach: run a cron job that dumps docker ps --format '{{.ID}},{{.Names}}' to a CSV that Splunk reads as a lookup table.
# Cron job on Docker host (every 5 minutes):
*/5 * * * * docker ps --format '{{.ID}},{{.Names}}' > /opt/splunk/etc/apps/search/lookups/docker_containers.csv
Proxmox task logs
Proxmox logs VM operations (start, stop, migrate, backup) to /var/log/pve/tasks/. These are structured enough to extract with regex:
# In /opt/splunk/etc/system/local/props.conf:
[pve_tasks]
EXTRACT-vmid = UPID:(?P<pve_node>[^:]+):(?P<pid>[^:]+):(?P<pstart>[^:]+):(?P<starttime>[^:]+):(?P<task_type>[^:]+):(?P<vmid>[^:]+):(?P<user>[^:]+)
SSH authentication events
Splunk's built-in linux_secure sourcetype handles most auth log parsing. Verify the extractions are working:
# Search for failed SSH logins with extracted fields:
index=main sourcetype=linux_secure "Failed password"
| stats count by src_ip, user
If src_ip and user aren't auto-extracted, add manual extractions in props.conf:
[linux_secure]
EXTRACT-ssh_failed = Failed password for (?:invalid user )?(?P<user>\S+) from (?P<src_ip>[\d.]+)
Want the complete infrastructure monitoring stack?
This post covers Splunk setup. The Infrastructure Guides Bundle includes full monitoring configurations for Proxmox, Docker, Tailscale, and backup systems — plus network diagrams, capacity planning templates, and tested configs you can deploy in an afternoon.
Get the Infrastructure Guides — $24 →04 Building Dashboards
Dashboards turn a pile of logs into a monitoring system. The goal isn't to visualize everything — it's to surface the five or six signals that tell you whether your lab is healthy without reading a single log line.
Homelab health overview
Start with a single dashboard that answers the morning question: "Is everything running?" Here are the panels that matter:
Panel 1: Events over time
index=main OR index=docker
| timechart span=1h count by index
A sudden drop in event volume usually means something stopped logging — which means something stopped running. A spike means something is wrong and screaming about it.
Panel 2: Failed SSH attempts (last 24h)
index=main sourcetype=linux_secure "Failed password"
| stats count by src_ip
| sort -count
| head 20
Panel 3: Docker container errors
index=docker "error" OR "fatal" OR "panic"
| stats count by source
| sort -count
Panel 4: Disk usage by host
If you're collecting system metrics via the Splunk Add-on for Unix (free), you get disk data automatically:
index=os sourcetype=df
| dedup host, Filesystem
| table host, Filesystem, UsePct
| where UsePct > 80
Panel 5: Proxmox VM operations
index=main sourcetype=pve_tasks
| stats count by task_type, vmid
| sort -count
Creating the dashboard
In the Splunk web UI: Search & Reporting → Dashboards → Create New Dashboard. Use the "Dashboard Studio" editor for the new XML-free layout, or Classic Dashboards if you prefer the traditional panel grid.
For each panel: run the search, click "Save As" → "Dashboard Panel", and add it to your overview dashboard. Set the time range to "Last 24 hours" for most panels.
Auto-refresh
Set the dashboard to auto-refresh every 5 minutes. In Classic Dashboard XML, add:
<dashboard refresh="300">
Now you have a single page that tells you the state of your lab at a glance.
05 SPL Queries That Actually Help
SPL (Search Processing Language) is what makes Splunk worth learning. It's a pipe-based language — similar to Unix shell pipelines — where each command transforms the result set for the next one. Once you internalize five or six commands, you can answer almost any question about your infrastructure in real time.
Find which hosts are noisiest
index=* earliest=-24h
| stats count by host
| sort -count
If one host generates 80% of your events, it's either logging too aggressively or something is broken. Both are worth investigating.
Track sudo usage across all hosts
index=main sourcetype=linux_secure "sudo:"
| rex "sudo:\s+(?P<sudo_user>\S+)"
| rex "COMMAND=(?P<sudo_cmd>.+)"
| table _time, host, sudo_user, sudo_cmd
| sort -_time
Detect port scans
If you're ingesting firewall logs (UFW, iptables, or pfSense), find hosts hitting multiple ports in a short window:
index=network sourcetype=ufw_log action=blocked
| bin _time span=5m
| stats dc(DPT) as unique_ports by _time, SRC
| where unique_ports > 10
| sort -unique_ports
Container restart frequency
index=docker "container started" OR "container stopped"
| rex field=source "/var/lib/docker/containers/(?P<container_id>[a-f0-9]{12})"
| stats count by container_id
| where count > 5
| sort -count
A container that restarts repeatedly is crash-looping. This query surfaces it before you notice the service is flapping.
Tailscale connection events
If you forward Tailscale logs to Splunk, track when nodes connect and disconnect:
index=main sourcetype=syslog "tailscaled"
| search "peer connected" OR "peer disconnected"
| table _time, host, _raw
06 Alerting Without Enterprise
Splunk Free doesn't include scheduled searches or built-in alerting. That's the one limitation that stings. But you can work around it with a simple cron job and Splunk's REST API.
The approach
Write a shell script that runs a Splunk search via the CLI, checks the results, and sends a notification (email, webhook, Telegram, Discord) if something is wrong. Schedule it with cron.
#!/bin/bash
# /opt/splunk/scripts/alert-failed-ssh.sh
RESULT=$(/opt/splunk/bin/splunk search 'index=main sourcetype=linux_secure "Failed password" earliest=-15m | stats count' -maxout 1 -output rawdata 2>/dev/null | tail -1)
if [ "$RESULT" -gt 20 ] 2>/dev/null; then
curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" \
-d chat_id="$TELEGRAM_CHAT_ID" \
-d text="⚠️ SSH Alert: $RESULT failed login attempts in the last 15 minutes"
fi
# Cron: run every 15 minutes
*/15 * * * * /opt/splunk/scripts/alert-failed-ssh.sh
Other alerts worth setting up
- Disk space > 85% — query the
dfsourcetype, alert before you hit 100% - No events from a host in 30 minutes — query
| metadata type=hosts | where recentTime < now()-1800 - Docker container crash loops — count restart events per container in the last hour
- New listening port detected — compare current
ss -tulnpoutput against a known-good baseline
It's not as elegant as Enterprise alerting with correlation searches and adaptive thresholds, but it works. For a homelab, a shell script that texts you when something is on fire is all you need.
07 Performance Tuning
Splunk on limited homelab hardware needs a few tweaks to stay responsive.
Control ingestion volume
The 500 MB daily limit sounds generous until you point Splunk at verbose Docker containers. Check your daily ingestion:
index=_internal source=*license_usage.log type=Usage
| stats sum(b) as bytes by idx
| eval MB=round(bytes/1024/1024,2)
| sort -MB
If you're burning through the limit, filter out noisy sources. In inputs.conf, use blacklists to skip health check logs and debug-level chatter:
[monitor:///var/lib/docker/containers/*/*-json.log]
blacklist = healthcheck|GET /health|GET /ready
Set retention policies
Don't store everything forever. Set per-index retention based on how much disk you can spare:
# In /opt/splunk/etc/system/local/indexes.conf:
[main]
frozenTimePeriodInSecs = 2592000
# 30 days
[docker]
frozenTimePeriodInSecs = 604800
# 7 days — container logs are high volume, low archival value
Memory and CPU limits
If Splunk is competing with other services on the same host, cap its resource usage in the systemd unit:
# /etc/systemd/system/Splunkd.service.d/override.conf
[Service]
MemoryMax=4G
CPUQuota=200%
sudo systemctl daemon-reload
sudo systemctl restart Splunkd
What We Didn't Cover
This gets you a working Splunk monitoring stack for your homelab. But a production-grade observability setup goes deeper. The full infrastructure bundle also covers:
- Metrics collection — CPU, memory, network throughput, and IOPS via collectd or Telegraf feeding into Splunk
- Proxmox API integration — pulling VM status, resource allocation, and backup job results directly from the Proxmox REST API into Splunk
- Network flow analysis — ingesting NetFlow/sFlow data from your switch or router for bandwidth monitoring and anomaly detection
- Backup monitoring — tracking Proxmox Backup Server job status, verifying backup integrity, and alerting on failures
- Capacity planning — trending queries and dashboards that show growth rates so you can buy hardware before you run out
- Multi-site monitoring — Splunk deployment across multiple locations with Tailscale as the transport layer
Get the Complete Infrastructure Guides Bundle
Full monitoring configs for Proxmox, Docker, Tailscale, and backup systems. Network diagrams, capacity planning templates, and tested deployment scripts. Everything you need to go from bare metal to fully observed infrastructure.
Download the bundle — $24 →Ongoing Maintenance
A monitoring system that nobody looks at is just a log sink. Build these habits:
- Daily: Glance at the health dashboard. Takes 30 seconds. If all panels look normal, move on.
- Weekly: Check license usage (
Settings → Licensing). If you're consistently above 400 MB/day, tune your inputs or add filtering. - Monthly: Review your saved searches and dashboards. Delete panels you never look at. Add searches for new infrastructure you've deployed.
- After adding new infrastructure: Add data inputs before you forget. A host that doesn't send logs to Splunk doesn't exist as far as your monitoring is concerned.
Monitoring is the difference between running a homelab and understanding it. Splunk Free gives you the same analytical power that enterprise SOC teams rely on — without the enterprise price tag. Point it at your logs, build a dashboard, and stop guessing what's happening on your network.