To use syslog in Linux, you read logs with journalctl or from the text files in /var/log, write your own entries with the logger command, and ship them off the box by adding a forwarding rule to rsyslog. On a modern server, the systemd journal collects every message first, and rsyslog turns those messages into plain files or forwards them to a central collector.
A plain-English way to picture it: syslog is the building's mailroom. Every program drops a short note in the slot (a log message), each note carries a department label (the facility, such as "authentication" or "mail") and an urgency stamp (the severity, from "emergency" down to "debug"), and the mailroom sorts the notes into the right pigeonholes, which are the files in /var/log. Forwarding means photocopying the notes and sending them to head office, so they still exist if someone burns down the branch.
The configs and outputs below come from a real AlmaLinux 9 server running rsyslog 8.2510, including a test where we took the central collector offline. Logs are the raw material of any log management program, so getting this layer right comes first.
What is syslog on a modern Linux server?
Syslog is both a message format and the logging service that collects those messages. The format is standardized in RFC 5424. On most current distributions, two programs share the job:
-
systemd-journald catches messages from services, the kernel and the
loggercommand, and stores them in a binary journal that you query withjournalctl. -
rsyslog reads from the journal and applies rules: write this kind of message to that file, send everything to that remote server.
Every message has a facility (which part of the system it came from) and a severity (how bad it is). Severities run from 0 to 7: emerg, alert, crit, err, warning, notice, info and debug. An rsyslog rule is written as facility.severity, and the severity means "this level and anything more urgent". So authpriv.* means every private authentication message, and *.err means errors and worse from any source.
| Facility | Code | What usually writes to it |
|---|---|---|
| kern | 0 | The kernel |
| user | 1 | Ordinary programs, and logger by default |
| daemon | 3 | Background services |
| auth | 4 | Security and authorization messages |
| cron | 9 | Scheduled jobs |
| authpriv | 10 | Private auth detail: sshd, sudo, PAM on most systems |
| local0 to local7 | 16 to 23 | Free for your own applications |
Where are the syslog files on Linux?
The file names depend on the distribution family, which is the first thing that trips people up when they move between servers.
| What you want | Debian / Ubuntu | RHEL / AlmaLinux / Rocky |
|---|---|---|
| Logins, sudo, SSH | /var/log/auth.log | /var/log/secure |
| General system messages | /var/log/syslog | /var/log/messages |
| Cron jobs | in /var/log/syslog | /var/log/cron |
| Main rsyslog rules | Ubuntu: /etc/rsyslog.d/50-default.conf; Debian: /etc/rsyslog.conf | /etc/rsyslog.conf |
Two caveats. First, some distributions no longer install rsyslog by default (Debian 12 is one), so /var/log/syslog simply does not exist and the journal is the only log. Run systemctl is-active rsyslog to find out which world you are in. Second, the journal itself may not survive a reboot. On our AlmaLinux 9 host it lives in /run/log/journal, which is held in memory, because /var/log/journal does not exist. There, the rsyslog files are the only copy that outlives a restart.
How do I read syslog in Linux?
Use journalctl for anything the journal holds, and tail or grep for the files. Both need root (or the adm group for the Debian and Ubuntu files; on RHEL-family systems the files are root-only). As an ordinary user, journalctl shows only your own messages and says so.
sudo journalctl -f sudo journalctl -u sshd --since "1 hour ago" sudo journalctl -p warning -b sudo journalctl -t sshd -n 50 --no-pager
In order: follow new messages live, show the SSH service for the last hour, show warnings and worse since the last boot, and show the last 50 messages tagged sshd. The full option list is in the journalctl manual. On the Debian / Ubuntu layout, swap the service name to ssh. For the text files:
sudo tail -f /var/log/secure sudo grep "Failed password" /var/log/secure | tail -20
How do I write my own messages to syslog?
Use logger, which sends a message into syslog with the tag, facility and severity you choose. It is the right way to log from a shell script, and the best way to test a rule before relying on it.
logger -t backup -p local0.err "nightly backup failed: disk full"Worked example: why a login warning missed /var/log/secure
Here is a trap we found while preparing this guide. Say a script logs a fake failed login with logger -t fieldguide -p auth.warning "failed login for user deploy". You would expect it in /var/log/secure. It will not be there. The reason sits in the stock AlmaLinux 9 rules, copied from the server:
*.info;mail.none;authpriv.none;cron.none action(type="omfile" file="/var/log/messages") authpriv.* action(type="omfile" file="/var/log/secure")
auth and authpriv are different facilities (codes 4 and 10). Only authpriv goes to /var/log/secure; a message on auth falls through to the first rule and lands in /var/log/messages. Debian and Ubuntu route both facilities to auth.log, so a script that "worked on Ubuntu" silently drops its security events into the general log on a RHEL-family box. If you write a detection that only watches /var/log/secure, it will never see them. The fix is to log with -p authpriv.warning, or to watch both files.
How do I forward syslog to a central server?
Add an omfwd action to rsyslog on each server, pointed at a collector, with a queue so messages wait instead of vanishing when the collector is down. Forwarding matters for security: an attacker who gets root can wipe local logs (we have covered attackers suppressing logs on a hypervisor), but cannot reach back into a copy already sent elsewhere.
If systemctl is-active rsyslog reports inactive on any machine, install it first with sudo apt install rsyslog or sudo dnf install rsyslog.
Step 1, the collector. On the central server, create a file that listens on TCP and writes one file per sending host and program:
module(load="imtcp") input(type="imtcp" port="514") template(name="PerHost" type="string" string="/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log") *.* action(type="omfile" dynaFile="PerHost")
The collector's own local messages match this rule too, so they also appear under /var/log/remote/<collector-hostname>/. On RHEL-family collectors with SELinux enforcing (the default), TCP port 514 is labeled for rsh, not syslog, so rsyslog is denied the port and nothing arrives. Relabel it, then open the firewall to your servers only (adjust the subnet):
sudo semanage port -m -t syslogd_port_t -p tcp 514 sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" port port="514" protocol="tcp" accept' sudo firewall-cmd --reload
On Ubuntu with ufw, the firewall equivalent is sudo ufw allow from 10.0.0.0/24 to any port 514 proto tcp, and no SELinux step is needed.
Step 2, each sending server. Forward everything over TCP with a queue. Replace the address with your collector's:
*.* action(type="omfwd" target="10.0.0.5" port="514" protocol="tcp" queue.type="LinkedList" queue.filename="fwd_central" queue.maxDiskSpace="1g" queue.saveOnShutdown="on" action.resumeRetryCount="-1")
LinkedList keeps the queue in memory, queue.filename lets it spill to disk when memory fills and on shutdown, and a retry count of -1 means keep trying forever instead of giving up. The rsyslog queue documentation explains the trade-offs.
Step 3, check the syntax, then restart. rsyslog will validate a config without loading it, which beats finding a typo after logs stop:
sudo rsyslogd -N1 sudo systemctl restart rsyslog
A clean run ends with End of config validation run. Bye.
Step 4, prove it end to end. From a sending server, log a line and look for it on the collector. Our test used port 10514 so it could run without root; the config was otherwise identical.
logger -t sshd -p authpriv.info "Failed password for invalid user admin from 203.0.113.7 port 52144 ssh2"2026-11-12T12:06:51.150065+02:00 suriqbot sshd Failed password for invalid user admin from 203.0.113.7 port 52144 ssh2
Then the test that matters: stop the collector, send three messages, start it again. With the queue in place, all three arrived about 40 seconds after the collector came back, because rsyslog retries on a timer rather than instantly. But the first message arrived twice:
2026-11-12T12:07:03+02:00 suriqbot sshd Failed password for root from 198.51.100.1 port 40001 ssh2 2026-11-12T12:07:03+02:00 suriqbot sshd Failed password for root from 198.51.100.2 port 40002 ssh2 2026-11-12T12:07:03+02:00 suriqbot sshd Failed password for root from 198.51.100.3 port 40003 ssh2 2026-11-12T12:07:03+02:00 suriqbot sshd Failed password for root from 198.51.100.1 port 40001 ssh2
Nothing was lost, which is the point of the queue. The duplicate comes from how plain TCP syslog works, and it leads into the limits below.
How do I get syslog into a SIEM like Wazuh?
There are two routes, and the choice depends on whether you can install software on the source. A SIEM (security information and event management system, explained in our guide to what a SIEM is) reads these logs and raises alerts on patterns such as repeated failed logins.
-
Install an agent on the server. The Wazuh agent reads files such as
/var/log/securedirectly, and can read the journal itself with ajournaldlog format, per the Wazuh localfile reference. This is the better route for servers; our Wazuh agent walkthrough covers setup. -
Send raw syslog to the Wazuh server. For devices you cannot install an agent on (firewalls, switches, appliances), point their syslog at the server and enable a listener:
<remote> <connection>syslog</connection> <port>514</port> <protocol>tcp</protocol> <allowed-ips>192.168.2.0/24</allowed-ips> </remote>
The Wazuh syslog documentation notes that the allowed-ips setting is mandatory: leave it out and the listener does not take effect. Restart with sudo systemctl restart wazuh-manager. Two cautions: if rsyslog on the same machine already listens on 514, pick another port, and once logs arrive, use wazuh-logtest to confirm a sample line is decoded and matched by the rule you expect.
Where syslog breaks
Syslog is old, simple and everywhere, which also means it fails quietly. The limits we plan around:
-
UDP loses messages without telling you. Classic syslog over UDP port 514 has no delivery confirmation (RFC 5426 says as much). A busy network or a restarting collector means silent gaps. Use TCP.
-
Plain TCP can duplicate or drop a message at reconnect. Our duplicate above is the visible half. The sender cannot be sure what the receiver got before a connection broke. When every event counts, use RELP, rsyslog's acknowledged protocol (the omrelp module), which needs the
rsyslog-relppackage on both ends. -
Plain syslog is unencrypted. Login names and IP addresses cross the network in clear text. Over anything but a private link, add TLS to the forwarding action.
-
Bursts get rate-limited. journald drops messages from a service that logs faster than its limit (
RateLimitBurstin journald.conf), and leaves a "Suppressed" note. A brute-force attack is exactly the kind of burst that can hit it. -
Local logs are only as safe as root. Anyone with root can edit or truncate
/var/log. Forwarding is what preserves the evidence.
None of this makes syslog a poor choice. It makes it a pipe that needs checking: confirm the files exist, test with logger, forward over TCP with a queue, and test that pipe again after any change.