The major Linux distributions write their logs to the systemd journal: a binary, indexed, local-first store.
However, your SIEM and collectors still expect syslog, which has been the plain text, line-oriented, network-native standard for more than four decades.
If you run SecOps, you have to bridge that gap on every Linux host you monitor, and the obvious-looking switch, ForwardToSyslog=yes, moves nothing off the host by itself.
Quick answer: how to forward journald to syslog?
journald stores logs in a binary journal and does not speak syslog over the network.
Either enable local hand-off to a syslog daemon with ForwardToSyslog=yes, or run a collector that reads the journal API and emits RFC 3164/5424 messages: rsyslog (imjournal module), syslog-ng (systemd-journal() source), or NXLog Agent (im_systemd module).
Remote syslog delivery always comes from a collector, never from journald itself.
This guide gives you working configurations for all four methods, explains what each one keeps or loses, and covers the hardening steps that matter before you rely on the pipeline in production.
What’s the difference between journald and syslog?
journald and syslog disagree on storage, transport, and structure. Those three differences are why you need a conversion step at all:
| Aspect | systemd journal | syslog |
|---|---|---|
Storage |
Binary, indexed journal files |
Plain text lines |
Structure |
Typed key-value journal fields ( |
Free-text message, although RFC 5424 adds optional structured data |
Network transport |
None in syslog terms (export format only) |
|
Read access |
|
Any text tooling, any SIEM |
The practical consequence for SecOps: journald holds richer metadata than a classic syslog line can carry. A good forwarding setup preserves that metadata, either as RFC 5424 structured data or as a JSON payload, instead of flattening everything to a bare message string.
Why journald alone doesn’t get logs off the box
The systemd journal is deliberately local.
It has two built-in options for moving messages elsewhere, and both carry documented limits per journald.conf(5):
- ForwardToSyslog
-
Copies each message to the local socket
/run/systemd/journal/syslog. The man page is blunt: if nothing reads from that socket, the setting has no effect. Nothing leaves the machine unless a local daemon picks the messages up and ships them itself. - ForwardToSocket
-
Moves entries between hosts, but in the Journal Export Format, not syslog. The
systemd-journal-upload/systemd-journal-remotepair does the same over HTTP. Thejournald.conf(5)man page also warns that socket forwarding runs synchronously inside journald and slows badly over IPv4/IPv6 links.
So if the destination is a syslog receiver or a SIEM with a syslog input, journald needs help. That help is a collector that reads the journal and writes syslog.
4 ways to forward journald to syslog
Four collectors can provide that help: journald’s own local hand-off, or a dedicated collector that reads the journal and emits syslog itself. Each trades off remote delivery, metadata fidelity, and operational overhead differently.
Method 1: Local forwarding with ForwardToSyslog
Use this method when a syslog daemon already runs on the host and you only need journald to hand over messages locally. Add this to journald’s configuration:
# /etc/systemd/journald.conf.d/forward.conf
[Journal]
ForwardToSyslog=yes
Then apply the change:
$ sudo systemctl restart systemd-journald
This approach has three caveats, all from journald.conf(5):
-
Forwarding targets the local socket only. If no daemon listens on
/run/systemd/journal/syslog, the setting does nothing. -
Upstream,
ForwardToSyslogdefaults to off. The man page states that only forwarding to wall is turned on by default. Distributions may override this, so check before assuming. -
The man page also notes that syslog daemons usually read the journal files directly instead of listening on this socket. That is exactly what the next three methods do, and it is why they also work when the daemon starts late in boot.
Verdict: Fine as a hand-off on a single host. It is not a remote forwarding mechanism, and it carries none of the journal’s structured metadata beyond the classic syslog fields.
Method 2: rsyslog with imjournal and omfwd
rsyslog reads the journal with the imjournal input module and ships it with omfwd.
Add this complete, production-oriented configuration to rsyslog:
# /etc/rsyslog.d/10-journal-forward.conf
module(load="imjournal"
StateFile="/var/lib/rsyslog/imjournal.state"
Ratelimit.Interval="60"
Ratelimit.Burst="60000")
# Only process journal messages, not everything rsyslog handles
if $inputname == "imjournal" then {
action(type="omfwd"
Target="siem.example.com"
Port="514"
Protocol="tcp"
# RFC 5424 output
Template="RSYSLOG_SyslogProtocol23Format"
TCP_Framing="octet-counted"
# Disk-assisted queue, survives restarts and outages
queue.type="LinkedList"
queue.filename="q_journal_fwd"
queue.maxDiskSpace="1g"
queue.saveOnShutdown="on"
action.resumeRetryCount="-1")
}
Then restart rsyslog:
$ sudo systemctl restart rsyslog
There are three behaviors you should plan around:
- Silent rate limiting
-
By default,
imjournalreads at most 20,000 messages per 600-second interval (Ratelimit.Burst 20000,Ratelimit.Interval 600) and discards the rest until the interval rolls over. rsyslog’s ratelimiter logs "imjournal: begin to drop messages due to rate-limiting" when it starts discarding, then "imjournal: N messages lost due to rate-limiting" when the interval closes, so you learn the size of the gap only after it has happened. A busy host, or one Docker container logging to journald, blows through that default. The configuration above lifts the ceiling to 60,000 messages per 60 seconds, about 1,000 per second against the default’s 33. Set both parameters, not just the burst. - rsyslog’s own reservations
-
The same documentation recommends the module "only if there is hard need to do so," because a corrupted journal database can make it duplicate messages endlessly, and suggests
imuxsock(the Method 1 socket) when journal metadata isn’t required. Cursor state lives in theStateFile, so keep it on persistent storage. - Framing choice
-
Both framing styles are legal per RFC 6587. Octet counting survives multiline messages; the LF-delimited default does not, which is the framing mismatch that shows up as truncated events at the receiver.
Method 3: syslog-ng with the systemd-journal() source
syslog-ng reads the journal API with the systemd-journal() source, forwarding RFC 5424 over TLS and keeping the journal’s metadata as structured data.
Add this to syslog-ng’s configuration:
# /etc/syslog-ng/conf.d/journal-forward.conf
source s_journal {
systemd-journal(prefix(".SDATA.journald."));
};
destination d_siem {
syslog("siem.example.com"
transport("tls")
port(6514)
tls(
ca-file("/etc/syslog-ng/certs/ca.pem")
key-file("/etc/syslog-ng/certs/client-key.pem")
cert-file("/etc/syslog-ng/certs/client-cert.pem")
)
# capacity-bytes() requires syslog-ng 4.3+; on older versions use disk-buf-size()
disk-buffer(capacity-bytes(1073741824) reliable(yes))
);
};
log { source(s_journal); destination(d_siem); };
Then restart syslog-ng:
$ sudo systemctl restart syslog-ng
The prefix(".SDATA.journald.") option carries the metadata: per the syslog-ng OSE administration guide, it maps every journal field into the RFC 5424 structured data of the outgoing message.
The same guide documents the constraints:
-
syslog-ng allows only one
systemd-journal()source per configuration; a second one stops syslog-ng from starting. -
The source reads local journals only; you cannot point it at another host’s journal files.
-
The guide cautions that the source cannot operate on Ubuntu 24.04 LTS (Noble Numbat). Check the supported platforms note against your fleet before standardizing on it.
-
The syslog-ng documentation steers you away from the socket-based alternative,
systemd-syslog(), calling systemd’s socket activation "buggy" and warning that messages can get lost during system startup.
Method 4: NXLog Agent with the Systemd input module
NXLog Agent reads the journal natively with the Systemd input module, which maps journal metadata (unit, PID, user, boot ID, SELinux context, and the rest) into named event fields you can filter on, rewrite, or forward. The Syslog extension module then generates BSD (RFC 3164), IETF (RFC 5424), or Snare syslog from those fields with a single procedure call.
This minimal configuration forwards the journal to a remote receiver in BSD syslog over TCP, matching our Linux log collection guide:
<Extension syslog>
Module xm_syslog
</Extension>
<Input journal>
Module im_systemd
</Input>
<Output siem>
Module om_tcp
Host siem.example.com:514
Exec to_syslog_bsd();
</Output>
<Route journal_to_siem>
Path journal => siem
</Route>
For production, add RFC 5424 output, TLS transport, and a queue that survives receiver outages and agent restarts, all as configuration directives:
define CERTDIR /opt/nxlog/var/lib/nxlog/cert
<Extension syslog>
Module xm_syslog
</Extension>
<Extension json>
Module xm_json
</Extension>
<Input journal>
Module im_systemd
# Starts from new entries only, then resumes from the saved position after restarts
ReadFromLast TRUE
# Optionally drop noise at the source, such as debug-level chatter
Exec if $SeverityValue >= 7 drop();
</Input>
<Output siem_tls>
Module om_ssl
Host siem.example.com:6514
CAFile %CERTDIR%/ca.pem
CertFile %CERTDIR%/agent-cert.pem
CertKeyFile %CERTDIR%/agent-key.pem
# RFC 5425 octet-counted framing over TLS
OutputType Syslog_TLS
# Disk-backed queue ensures events survive outages and restarts
PersistLogqueue TRUE
# Pack the full journal record into the message as JSON, then emit an RFC 5424 syslog line
<Exec>
$Message = to_json();
to_syslog_ietf();
</Exec>
</Output>
<Route journal_to_siem>
Path journal => siem_tls
</Route>
Why this holds up in a SecOps pipeline:
- Metadata survives the conversion
-
to_syslog_ietf()carries custom event fields into RFC 5424 structured data, and theto_json()line above keeps the complete journal record, every fieldim_systemdextracted, inside the message body. Your SIEM gets the unit name and user, not just a text line. Note the cost, though: with both in place, every field ships twice, once in structured data and once in the JSON body, which roughly doubles the message size. Drop the$Message = to_json()assignment if structured data alone is enough for your receiver. - Framing is one directive
-
OutputType Syslog_TLSselects RFC 5425 octet-counted framing, which avoids the truncated-multiline-message problems of newline-delimited TCP syslog. Both framing styles are covered in our syslog integration guide, and the operational details of TLS forwarding are in our post on syslog forwarding over TLS. - Buffering is on by default
-
NXLog Agent applies log queues and flow control out of the box;
PersistLogqueueTRUEmoves the queue to disk so a SIEM outage doesn’t cost you events. - One agent, one configuration language, every OS
-
The same agent that reads journald also collects Windows Event Log, flat files, and dozens more sources through 120+ built-in modules, so your Linux journal pipeline and your Windows pipeline are one skill set, not two.
And when you run this on more than a handful of hosts, NXLog Platform manages it centrally: you edit the configuration once, push it to every enrolled agent, and monitor agent health from one place instead of shelling into boxes. The NXLog Platform documentation covers enrollment and fleet configuration.
Which method should you use?
| ForwardToSyslog | rsyslog | syslog-ng | NXLog Agent | |
|---|---|---|---|---|
Remote delivery |
No, local socket only |
Yes |
Yes |
Yes |
RFC 5424 output |
Depends on local daemon |
Yes (template) |
Yes ( |
Yes ( |
TLS transport |
N/A |
Yes, via stream driver setup |
Yes ( |
Yes ( |
Journal metadata preserved |
No |
Partially (JSON properties, extra templating) |
Yes ( |
Yes (event fields to structured data or JSON) |
Documented gotchas |
Local-only; off by default upstream |
20k/10min silent rate limit; vendor discourages the module |
One source per config; cannot run on Ubuntu 24.04 LTS |
Commercial; free plan covers up to 10 sources |
Central fleet management |
No |
No (config management is on you) |
No (config management is on you) |
Yes, via NXLog Platform |
Same tooling on Windows/other OS |
No |
No |
Partially (PE) |
Yes |
For a single Linux box that already runs rsyslog or syslog-ng, either daemon will move journal entries to a remote receiver, and their limitations are documented and manageable. The case for NXLog Agent gets stronger with scale and heterogeneity: the same collection layer on Linux and Windows, structured metadata at the SIEM without template engineering, disk-backed delivery, and one console (NXLog Platform) to configure and monitor the fleet instead of per-host config files.
Hardening checklist before you trust the pipeline
Whichever collector you choose, journald sits upstream of it, and journald’s defaults can drop data before your forwarder ever sees it.
The settings below come from journald.conf(5):
# /etc/systemd/journald.conf.d/hardening.conf
[Journal]
# Set persistent storage explicitly
Storage=persistent
# Default is 10,000 messages per 30s per service, and excess is dropped.
# Size for your noisiest legitimate service.
RateLimitIntervalSec=30s
RateLimitBurst=50000
The following practices round out the hardening checklist:
- Rate limiting drops at intake
-
If a service exceeds the burst, journald discards the excess. Those messages never reach the journal, so no forwarder can recover them. Tune the limits, and watch for "Suppressed N messages" entries.
- Prefer TCP or TLS over UDP
-
Classic UDP syslog gives no delivery guarantee. For TCP, decide on framing (octet counting or newline delimiting, both described in RFC 6587) and make sure sender and receiver agree. For TLS, RFC 5425 with octet counting on port 6514 is the standard pairing.
- Buffer at the sender
-
Receiver maintenance windows happen. Disk-assisted queues (rsyslog),
disk-buffer()(syslog-ng), andPersistLogqueue(NXLog Agent) are the difference between a delay and a gap in your evidence trail. - Filter at the edge, not the SIEM
-
Dropping debug-level noise on the host (see the
drop()line in the NXLog Agent config above) cuts transport and ingestion costs without touching what you keep locally in the journal.
How to verify journald-to-syslog forwarding
Generate a marked test message, confirm journald has it, then confirm the receiver got it:
# 1. Write a test event through the normal logging path
$ logger -t fwd-test -p auth.notice "journald to syslog pipeline check $(date +%s)"
# 2. Confirm it landed in the journal
$ journalctl -t fwd-test -n 3 --no-pager
# 3. Confirm it left the host (run on the sender)
# Plain TCP on 514: -A prints the payload, so you can read the test message.
$ sudo tcpdump -A -c 5 host siem.example.com and port 514
# TLS on 6514: the payload is encrypted. Count packets instead of reading them.
$ sudo tcpdump -c 5 host siem.example.com and port 6514
# 4. Confirm it arrived (on the receiver / SIEM search)
If step 2 works but step 3 shows nothing, check the usual suspects in order: the collector isn’t reading the journal (service down, imjournal rate limit hit, state file pointing past the message), the route or action is misconfigured, or, with Method 1, you expected ForwardToSyslog to do network delivery it was never designed for.
Truncated or merged multiline messages at the receiver point to a framing mismatch; timestamps off by hours point to local-time RFC 3164 output where the receiver expected UTC or RFC 5424.
On the TLS path, packets on 6514 show only that bytes are leaving the host.
Step 4 at the receiver is what confirms the message itself arrived and parsed.
Ship your journal the way your SIEM expects
NXLog Agent reads journald natively, converts to the syslog flavor your receiver wants, and delivers it over TLS with disk-backed queues. NXLog Platform keeps every agent’s configuration in one place. Try NXLog Platform for free, or start with the Systemd input module reference and the Linux log collection guide. If you run into trouble along the way, reach out and we’ll help you sort it out.
FAQ
- Does journald replace syslog?
-
No. journald replaces the local storage role of a syslog daemon on systemd distributions, but it doesn’t speak the syslog network protocol. Per
journald.conf(5), it can hand messages to a local syslog daemon or export them in Journal Export Format. syslog delivery to a remote server requires a collector. - Does ForwardToSyslog send logs to a remote server?
-
No.
ForwardToSyslog=yeswrites messages to the local socket/run/systemd/journal/syslog. If no local daemon reads that socket, the setting has no effect, and journald does not transmit syslog over the network either way. A daemon that reads the socket can forward onward, but that is the daemon’s work, not journald’s. - How do I forward journald to a remote syslog server?
-
Run a collector on the host that reads the journal and emits syslog: rsyslog with
imjournal, syslog-ng withsystemd-journal(), or NXLog Agent with the Systemd input module plusto_syslog_ietf(), forwarded with either the TCP or TLS/SSL output module. Working configurations for all three are above. - What port does syslog over TLS use?
-
Port 6514, per RFC 5425, which also specifies octet-counted framing for TLS transport. Plain TCP syslog commonly uses port 514 with either octet counting or newline framing (RFC 6587). Whichever pairing you pick, configure the sender and receiver to match.
- Can I keep journald’s structured fields when converting to syslog?
-
Yes. RFC 5424 structured data elements carry key-value pairs. syslog-ng maps journal fields with the
.SDATA.journald.prefix, and NXLog Agent’sto_syslog_ietf()includes event fields as structured data. You can also serialize the full record to JSON in the message body.