rsyslog: relay messages only (no local storage)

This tutorials tells how rsyslog is configured to accept syslog messages over the network via UDP. No advanced topics are covered. We use CentOS 7. This is part of a rsyslog tutorial series.


We will configure LC to only relay messages received via UDP but not store them locally.  Locally-generated messages will still be stored inside local log files. They, too, will be forwarded to LR. This is a very common use case. We still do not configure any sender to connect to LC.

To do all of this, we need to modify only LC local configuration. As such, our base lab scenario will remain in the following configuration:

Note that we still do not configure any system to actually send data to LC. This will be done the next tutorial. Note that if you did not complete the last tutorial, it may be wise to have a look at it. We will work with the configuration it generated.


You need to

Configuration Format

We “configured” the current UDP server configuration by just uncommenting Red-Hat provided sample configuration statements (they should now be without the hash signs in your config).

As we discussed, these statements are in obsolete legacy format. We told you that this were fine “for the time being”.

Now things change. There are several ways to achieve our goal. The best method is to use a dedicated rule set: it is easy to understand, fast and highly flexible. Also, using a dedicated rule set means you do not need to mess up with the default log files.

However, doing multiple rule sets in obsolete legacy format is painful and error-prone. If you want to know what I mean, have a look at the documentation for rulesets done with legacy format. Note: this is from the heavily outdated v5 documentation. The newer doc does not use the outdated constructs.

Take a couple of minutes to read and digest the legacy format rule set definition. Then have a look at how rsyslog rule sets are created in advanced format. Compare the examples. Which one do you think is easier to grasp, create and maintain?

As such, we will use advanced format for our new rule set. This is also the right time to keep things consistent and convert the other affected parts to advanced format as well. Do not be scared: this is a simple process.

Changes to the Input Definition

The config file contains these lines to define the server:

$ModLoad imudp
$UDPServerRun 514

The first one loads the imudp module, which essentially is the UDP server code. Loading modules in advanced format is done via the so-called “module()” object. Converting to new format is simple and straightforward as no module parameters are set. It just becomes


The $UDPServerRun statement creates the actual server. In advanced format, this is done via the “input()” object. If you look at the imudp documentation, you will find the legacy format statement together with its current counterpart. Again, we have a very simple configuration, so the fully converted input part of our configuration is just:

input(type="imudp" port="514")

That’s it as far as the input is concerned. Note that this converts the config exactly as-is. Most importantly, no support for rule sets has been added yet.

I suggest to

  • apply the configuration change
  • make rsyslog use the new configuration
  • check that rsyslog continues to work

Adding the Rule Set

Now it is time to ensure that UDP-received messages are no longer stored in local log files on LC. For this, we add a rule set  and make the udp server submit messages directly to it.

The rule set can simply be created by wrapping the configuration statements within a ruleset() object:

Rule Set is added and contains the legacy action.

That also shows how legacy and advanced format can be combined. There are ample of possibilities. Please note that at this stage we strongly recommend to convert the legacy format action as well. This will be described later in this tutorial.

For the time being, we will continue to use the legacy action within the rule set. While we have defined the rule set, we have not yet actually used it. In our case this means we need to make the udp server use it. This can simply be done on the input() line:

input(type="imudp" port="514" ruleset="sendRemote")

This almost concludes the configuration changes we intended in this step. What does that mean?

The rsyslog configuration now contains two rule sets: one (“sendRemote”) we explicitly created by our config statements. It is applied to (“bound” in rsyslog speak) the udp server. That means any message the server receives will go directly to that rule set. As it only contains the forwarding action, messages received via UDP will no longer be stored in the local log files.

The other rule set inside the config is implicitly defined by rsyslog. It is the so-called default rule set. It always exists and any action not part of any user-defined rule set is contained in it. In our case, all the file writing rules are contained in it. The default rule set is used by all inputs which do not have an explicit rule set bound. In our case, this means everything but the udp server. As such, regular log messages are written to local files.

When you check your configuration, you will notice that local messages are no longer forwarded to the server LR. This is because the forwarding rule is no longer part of the default rule set. And as such it is no longer executed for local messages.

How to fix that? The brute-force approach is adding an additional forwarding rule to the default rule set – usually by means of copy&paste. While this is frequently done, it is a bad solution. Whenever you need to change forwarding parameters you need to do it twice. Experience tells one place or the other is often forgotten.

Rsyslog offers a very simple tool to do this more elegantly: the “call” statement permits a rule set to call another one. Just like a function in a scripting language. So the proper cure is to call our new ruleset “sendRemote” after the local files are written. This will look like follows:

Remote rule set is now also called from default one (highlighted line).

With this configuration local messages are once again both recorded in local files and forwarded to LR.

If you have not already done so,

  • apply the configuration changes
  • make rsyslog use the new configuration
  • check that rsyslog continues to work (you may want to try with and without the “call” statement).

Completing the Conversion

Our objectives are already meet. However, we still have legacy format inside our “sendRemote” rule set. The action called there has several parameters, and this is something that calls for problems in legacy format. So we really should take the opportunity and convert the action as well. In the long term, we will be rewarded by much better maintainability.

The action definition uses several configuration parameters: from the module that carries out the action, from the action itself as well as for the queue which is used for the action. As such, we do not have a single place to check for the mapping of legacy format statements to advanced ones.

A good starting point configuration format update is the “Converting from older to advanced format” rsyslog documentation page. Note that this page also has a dedicated section on action conversion.

If you have time, I suggest that you read up on the conversion documentation and try to do the conversion yourself. Then, read on and check what we suggest with what you have. While this takes some extra effort, trying to do it yourself will improve your understanding of the process.

Now let me show what we need to look at. Our action is defined as follows:

$ActionQueueFileName fwdRule1
$ActionQueueMaxDiskSpace 1g
$ActionQueueSaveOnShutdown on
$ActionQueueType LinkedList
$ActionResumeRetryCount -1
*.* @@

The first thing you need to understand is that all of these statements make up a single action. This is just the (hard-to-understand) way legacy format passes parameters. You notice parameters starting with “$ActionQueue” – all of them change queue settings. You can look up the new-style equivalents in the queue documentation. Do an on-page search via the browser to get quickly to the parameter in question. For example “$ActionQueueFileName” will become “queue.fileName”.

The other settings that start with “$Action” are usually action object parameters. Also look them up. The last line is what usually is considered “the action”. You can look up the various formats in the conversion doc. After conversion, the action looks like this:

action(type="omfwd" target="" port="514" protocol="tcp" action.resumeRetryCount="-1" queue.type="linkedList" queue.saveOnShutdown="on" queue.maxDiskSpace="1g" queue.fileName="fwdRule1")

The config file as whole looks much cleaner and clearer now. Here is the relevant part:

Rule set and call statement after conversion.

You should now

  • make the necessary configuration changes on LC if not already done
  • activate them
  • check that everything works (both for local and remote messages)

If all goes well, you should see something similar to this:

Local messages only on LC (top) and all messages on LR (bottom).

Make sure you check that messages received via UDP will not be logged to LC’s local log file, but are visible on LR. Also check that local messages from LC show up in LR’s log as well.

If something does not work as expected, check your configuration carefully. There may be a simple typo (which systemctl status rsyslog should show you). Or maybe some missing parameters. Hint: if you forget ‘protocol=”tcp”‘ in the action definition, messages will be sent via UDP and not be delivered. For UDP, this will not generate an error message (by udp design). This kind of issue maybe hard to find.


System LC now acts as a local relay (for udp syslog) only. It does not write messages received via UDP to the local log files. Local logs generated on LC are still written to it’s own log files and they are also forwarded to LR. This is a use case very commonly found.

In order to achieve this functionality, we converted the relevant parts of our configuration to the current advanced configuration format. That format is more user-friendly and precise. Using it also greatly simplifies the configuration of our use case.