Syslog Integration

Collect logs from Proxmox, VMware ESXi, firewalls, routers, and any device that supports syslog. Perfect for home labs and infrastructure monitoring.

Home Lab Ready

This guide is based on real-world configurations tested with Proxmox, ESXi, UniFi, and various network devices. Works with both RFC 3164 (traditional) and RFC 5424 (modern) syslog formats.

Overview

LogWard can receive syslog messages via Fluent Bit, which acts as a syslog server listening on port 514 (UDP/TCP). This allows you to centralize logs from:

Hypervisors & Servers
  • • Proxmox VE
  • • VMware ESXi
  • • XCP-ng
  • • Linux servers (rsyslog/syslog-ng)
  • • TrueNAS / FreeNAS
Network Devices
  • • UniFi (UDM, switches, APs)
  • • pfSense / OPNsense
  • • MikroTik routers
  • • Cisco switches/routers
  • • Synology NAS

Fluent Bit Configuration

Add syslog inputs to your Fluent Bit configuration. Create these files in your LogWard directory alongside docker-compose.yml.

fluent-bit.conf

Complete configuration with Docker logs AND syslog support:

conf
# Fluent Bit Configuration for LogWard
# Supports Docker container logs AND syslog from network devices

[SERVICE]
    Flush        5
    Daemon       Off
    Log_Level    info
    Parsers_File /fluent-bit/etc/parsers.conf

# =============================================================================
# INPUT - Docker Container Logs
# =============================================================================
[INPUT]
    Name              tail
    Path              /var/lib/docker/containers/*/*.log
    Parser            docker
    Tag               docker.*
    Refresh_Interval  5
    Mem_Buf_Limit     5MB
    Skip_Long_Lines   On
    Path_Key          filepath

# =============================================================================
# INPUT - Syslog (UDP) - Port 514
# =============================================================================
# Receive syslog messages on UDP (Proxmox, ESXi, firewalls, etc.)
[INPUT]
    Name        syslog
    Parser      syslog-rfc3164
    Listen      0.0.0.0
    Port        514
    Mode        udp
    Tag         syslog.udp

# =============================================================================
# INPUT - Syslog (TCP) - Port 514
# =============================================================================
# TCP is more reliable for important logs
[INPUT]
    Name        syslog
    Parser      syslog-rfc3164
    Listen      0.0.0.0
    Port        514
    Mode        tcp
    Tag         syslog.tcp

# =============================================================================
# FILTER - Docker Logs Processing
# =============================================================================
[FILTER]
    Name                parser
    Match               docker.*
    Key_Name            log
    Parser              docker_json
    Reserve_Data        On
    Preserve_Key        On

[FILTER]
    Name                lua
    Match               docker.*
    script              /fluent-bit/etc/extract_container_id.lua
    call                extract_container_id

[FILTER]
    Name                modify
    Match               docker.*
    Add                 level info
    Rename              log message
    Copy                container_name service

[FILTER]
    Name                record_modifier
    Match               docker.*
    Remove_key          stream
    Remove_key          filepath
    Remove_key          container_name

# =============================================================================
# FILTER - Syslog Processing
# =============================================================================
[FILTER]
    Name                modify
    Match               syslog.*
    Copy                ident service
    Add                 level info

# Map syslog severity to log level
[FILTER]
    Name                lua
    Match               syslog.*
    script              /fluent-bit/etc/map_syslog_level.lua
    call                map_syslog_level

[FILTER]
    Name                record_modifier
    Match               syslog.*
    Remove_key          pri
    Remove_key          ident
    Remove_key          pid

# =============================================================================
# OUTPUT - Send to LogWard API
# =============================================================================
[OUTPUT]
    Name                http
    Match               docker.*
    Host                ${LOGWARD_API_HOST}
    Port                8080
    URI                 /api/v1/ingest/single
    Format              json_lines
    Header              X-API-Key ${LOGWARD_API_KEY}
    Header              Content-Type application/json
    Json_date_key       time
    Json_date_format    iso8601
    Retry_Limit         3
    tls                 Off

[OUTPUT]
    Name                http
    Match               syslog.*
    Host                ${LOGWARD_API_HOST}
    Port                8080
    URI                 /api/v1/ingest/single
    Format              json_lines
    Header              X-API-Key ${LOGWARD_API_KEY}
    Header              Content-Type application/json
    Json_date_key       time
    Json_date_format    iso8601
    Retry_Limit         3
    tls                 Off

parsers.conf

Parser definitions for Docker and both syslog formats:

conf
# Fluent Bit Parsers Configuration

# =============================================================================
# PARSERS - Docker
# =============================================================================
[PARSER]
    Name        docker_json
    Format      json
    Time_Key    time
    Time_Format %Y-%m-%dT%H:%M:%S.%L%z
    Time_Keep   On

[PARSER]
    Name        docker
    Format      json
    Time_Key    time
    Time_Format %Y-%m-%dT%H:%M:%S.%L%z
    Time_Keep   On

# =============================================================================
# PARSERS - Syslog
# =============================================================================
# RFC 3164 - Traditional syslog format
# Used by: Proxmox, many Linux systems, older network devices
[PARSER]
    Name        syslog-rfc3164
    Format      regex
    Regex       /^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$/
    Time_Key    time
    Time_Format %b %d %H:%M:%S
    Time_Keep   On

# RFC 5424 - Modern syslog format
# Used by: Modern Linux systems, VMware ESXi, some firewalls
[PARSER]
    Name        syslog-rfc5424
    Format      regex
    Regex       /^\<(?<pri>[0-9]{1,5})\>1 (?<time>[^ ]+) (?<host>[^ ]+) (?<ident>[^ ]+) (?<pid>[-0-9]+) (?<msgid>[^ ]+) (?<extradata>(\[.*\]|-)) (?<message>.+)$/
    Time_Key    time
    Time_Format %Y-%m-%dT%H:%M:%S.%L%z
    Time_Keep   On

map_syslog_level.lua

Lua script to convert syslog severity (0-7) to LogWard log levels:

lua
-- Map syslog priority/severity to log level
-- Syslog severity levels (from RFC 3164/5424):
-- 0 = Emergency, 1 = Alert, 2 = Critical, 3 = Error
-- 4 = Warning, 5 = Notice, 6 = Informational, 7 = Debug

function map_syslog_level(tag, timestamp, record)
    local pri = tonumber(record["pri"])

    if pri then
        -- Extract severity from priority (severity = pri % 8)
        local severity = pri % 8

        -- Map severity number to log level string
        local level_map = {
            [0] = "emergency",
            [1] = "alert",
            [2] = "critical",
            [3] = "error",
            [4] = "warning",
            [5] = "notice",
            [6] = "info",
            [7] = "debug"
        }

        record["level"] = level_map[severity] or "info"
    else
        record["level"] = "info"
    end

    -- Set 'time' field in ISO8601 format
    record["time"] = os.date("!%Y-%m-%dT%H:%M:%SZ", timestamp)

    -- Keep hostname from syslog
    if record["host"] and record["host"] ~= "-" then
        record["hostname"] = record["host"]
    end

    -- Use ident (program name) as service if available
    if not record["service"] and record["ident"] and record["ident"] ~= "-" then
        record["service"] = record["ident"]
    elseif not record["service"] and record["hostname"] then
        record["service"] = record["hostname"]
    else
        record["service"] = "syslog"
    end

    -- Clean up fields
    record["pri"] = nil
    record["ident"] = nil
    record["host"] = nil

    return 1, timestamp, record
end

extract_container_id.lua

Lua script to extract container ID from Docker log paths:

lua
-- Extract container ID from Docker log filepath
-- Path format: /var/lib/docker/containers/CONTAINER_ID/CONTAINER_ID-json.log

function extract_container_id(tag, timestamp, record)
    local filepath = record["filepath"]

    if filepath then
        local container_id = filepath:match("/var/lib/docker/containers/([^/]+)/")

        if container_id then
            record["container_id"] = container_id
            record["container_short_id"] = container_id:sub(1, 12)
        end
    end

    return 1, timestamp, record
end

Docker Compose Setup

Add Fluent Bit to your docker-compose.yml with syslog ports exposed:

yaml
services:
  # ... your other services (postgres, redis, backend, frontend) ...

  fluent-bit:
    image: fluent/fluent-bit:latest
    container_name: logward-fluent-bit
    ports:
      - "514:514/udp"  # Syslog UDP
      - "514:514/tcp"  # Syslog TCP
    volumes:
      # Configuration files
      - ./fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro
      - ./parsers.conf:/fluent-bit/etc/parsers.conf:ro
      # Lua scripts
      - ./extract_container_id.lua:/fluent-bit/etc/extract_container_id.lua:ro
      - ./map_syslog_level.lua:/fluent-bit/etc/map_syslog_level.lua:ro
      # Docker logs access
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      LOGWARD_API_KEY: ${FLUENT_BIT_API_KEY}
      LOGWARD_API_HOST: backend
    depends_on:
      backend:
        condition: service_healthy
    restart: unless-stopped

Port 514 Requires Root

Port 514 is a privileged port. If running Docker without root, use a higher port (e.g., 5514) and configure your devices to send to that port instead.

Proxmox VE Setup

Configure Proxmox to send syslog messages to LogWard via rsyslog:

1. Install rsyslog (if not installed)

bash
apt install rsyslog -y

2. Create rsyslog configuration

Create /etc/rsyslog.d/50-logward.conf:

bash
# Replace with your LogWard server IP
*.* @@10.0.0.100:514

Use @@ for TCP (recommended) or @ for UDP.

3. Restart rsyslog

bash
systemctl restart rsyslog
systemctl status rsyslog

4. Test the connection

bash
logger -t proxmox-test "Test log from Proxmox to LogWard"

Check LogWard - you should see the test message with service "proxmox-test".

VMware ESXi Setup

Configure ESXi to forward syslog to LogWard:

Via Web UI (vSphere Client)

  1. Navigate to Host → Manage → System → Advanced Settings
  2. Find Syslog.global.logHost
  3. Set value to: udp://YOUR_LOGWARD_IP:514 or tcp://YOUR_LOGWARD_IP:514
  4. Click Save

Via SSH/CLI

bash
# Enable SSH on ESXi first, then:
esxcli system syslog config set --loghost='tcp://10.0.0.100:514'
esxcli system syslog reload

ESXi Firewall

You may need to enable the syslog firewall rule: esxcli network firewall ruleset set --ruleset-id=syslog --enabled=true

Other Devices

UniFi (UDM/USG)

  1. Go to Settings → System → Remote Logging
  2. Enable Remote Syslog Server
  3. Enter your LogWard server IP and port 514
  4. Select log level (Info recommended)

pfSense / OPNsense

  1. Go to Status → System Logs → Settings
  2. Check Enable Remote Logging
  3. Enter your LogWard server IP in Remote log servers
  4. Select which logs to send (firewall, system, etc.)

Linux Servers (rsyslog)

Same as Proxmox - create /etc/rsyslog.d/50-logward.conf:

bash
# Forward all logs to LogWard (TCP)
*.* @@YOUR_LOGWARD_IP:514

# Or forward only specific facilities/severities:
# auth,authpriv.* @@YOUR_LOGWARD_IP:514    # Auth logs only
# *.err @@YOUR_LOGWARD_IP:514              # Errors and above only

Synology NAS

  1. Go to Control Panel → Log Center → Log Sending
  2. Check Send logs to a syslog server
  3. Enter your LogWard server IP
  4. Select format: BSD (RFC 3164) or IETF (RFC 5424)

Troubleshooting

Syslog messages not appearing?

  1. Verify Fluent Bit is running: docker compose ps fluent-bit
  2. Check Fluent Bit logs: docker compose logs fluent-bit
  3. Test connectivity from source: nc -zv YOUR_LOGWARD_IP 514
  4. Check firewall allows port 514 (TCP/UDP)
  5. Verify API key is set in .env: FLUENT_BIT_API_KEY=lp_...

Test syslog manually

Send a test syslog message using netcat:

bash
# UDP test
echo "<14>Test syslog message from terminal" | nc -u -w1 YOUR_LOGWARD_IP 514

# TCP test
echo "<14>Test syslog message from terminal" | nc -w1 YOUR_LOGWARD_IP 514

Service name shows as "syslog"?

The service name comes from the syslog "ident" field (program name). If your device doesn't send an ident, the hostname will be used. You can customize the Lua script to set service names based on hostname patterns.

Security Detection with Sigma Rules

Once syslog is flowing into LogWard, you can use Sigma rules to detect security threats. LogWard supports the industry-standard Sigma format for log detection rules.

Example: Detect SSH Brute Force
yaml
title: SSH Authentication Failure
status: stable
logsource:
    product: linux
    service: sshd
detection:
    selection:
        message|contains:
            - 'Failed password'
            - 'authentication failure'
    condition: selection
level: medium

Learn more about Sigma rules in the API documentation.

You're All Set!

Your infrastructure logs are now flowing into LogWard. Use the search and filter features to explore your logs, set up alerts for important events, and enable Sigma rules for security monitoring.

This guide was inspired by Brandon Lee's excellent article "LogWard Is the Lightweight Syslog Server Every Home Lab Needs in 2025" on VirtualizationHowto.