YAML Configuration Format

rsyslog supports YAML as an additional configuration format for YAML-centric environments. YAML and RainerScript express the same core rsyslog model and share the same semantics.

rsyslog supports configuration via YAML files as an additional format alongside RainerScript. It is one of the supported configuration formats and is a good fit when rsyslog is part of a broader YAML-centric operational workflow.

Typical examples include Kubernetes deployments, Ansible-managed systems, GitOps-style repositories, generated configuration, and AI-assisted infrastructure workflows. In those environments YAML support reduces integration friction because rsyslog can stay in the same format family as the surrounding tooling instead of relying on conversion steps or embedded RainerScript text.

This is about workflow fit, not about replacing RainerScript. The two formats express the same core rsyslog concepts, including inputs, actions, rulesets, filters and routing, templates, and queues. Every YAML configuration key maps directly to a RainerScript parameter of the same name, so all per-module documentation remains applicable unchanged. You can also mix formats: a YAML main config may include RainerScript .conf fragments and vice versa.

Continuity with RainerScript

Existing RainerScript configurations remain fully valid and fully supported. You do not need to migrate them in order to use current rsyslog features.

Existing RainerScript knowledge also transfers directly. YAML changes the surface syntax, but it does not introduce a separate rsyslog model or separate runtime semantics.

If you want to migrate an existing RainerScript config into YAML, rsyslog can generate canonical YAML directly via rsyslogd -N1 -F yaml -o .... Simple rulesets are emitted as structured YAML such as actions: or filter: + actions:, and a limited set of common legacy selector/action forms is normalized into structured YAML statements:. More complex bodies still fall back to script: |. See Translating Between RainerScript and YAML for the workflow and caveats.

When to Use YAML or RainerScript

Choose the format that fits your environment and workflow:

  • Use YAML when rsyslog is part of a broader YAML-centric deployment or automation workflow.

  • Use RainerScript when you already work effectively with it, when your team authors rsyslog logic directly, or when it is simply the clearer fit for your operational model.

  • Mix formats when that is practical. YAML and RainerScript can include each other and share the same object names and parameter semantics.

Activation

rsyslog automatically selects the YAML loader whenever the configuration file name ends in .yaml or .yml. No special flag or directive is required:

rsyslogd -f /etc/rsyslog.yaml

The same detection applies to files pulled in via include() (from a RainerScript config) or the include: section of a YAML config. It is therefore valid to mix formats: a YAML main config may include .conf RainerScript fragments and vice versa.

Note

YAML configuration support requires rsyslog 8.2604.0 or later. Older distribution packages do not include this feature. Up-to-date packages for many platforms are available via www.rsyslog.com. The easiest option is often a containerized build: the rsyslog team publishes ready-to-use images documented in Rsyslog Containers.

In addition, rsyslog must have been compiled with libyaml (yaml-0.1). If libyaml is absent at build time the daemon will print an error and refuse to load a .yaml / .yml file. Install the libyaml-dev (Debian/Ubuntu) or libyaml-devel (RHEL/Fedora) package before compiling.

Schema Overview

A YAML config file is a single YAML mapping (dictionary) whose top-level keys correspond to rsyslog configuration object types:

version: 2          # optional, informational only

global:   { ... }
modules:  [ ... ]
inputs:   [ ... ]
templates: [ ... ]
rulesets: [ ... ]
mainqueue: { ... }
include:  [ ... ]
parsers:  [ ... ]
lookup_tables: [ ... ]
dyn_stats: [ ... ]
perctile_stats: [ ... ]   # note: spelt "perctile" — matches rsyslog internal naming
ratelimits: [ ... ]
timezones: [ ... ]

All section names are case-sensitive. Unknown top-level keys are logged as errors and ignored. Each top-level key must appear at most once; duplicate keys are undefined behaviour in the YAML specification and unsupported by rsyslog — a warning is logged if a duplicate is detected. To specify multiple items of the same type use a sequence under one key, or use include: for file-level composition.

Singleton Sections

global and mainqueue are singleton sections: their value is a mapping of parameter names to values.

global:
  workdirectory: "/var/spool/rsyslog"
  maxmessagesize: 8192
  defaultnetstreamdriver: "gtls"
  privdrop.user.name: "syslog"
  privdrop.group.name: "syslog"

mainqueue:
  type: linkedlist
  size: 100000
  dequeuebatchsize: 1024

Parameter names are identical to their RainerScript counterparts (see global configuration parameters and queue parameters).

Sequence Sections

modules, inputs, templates, rulesets, parsers, lookup_tables, dyn_stats, perctile_stats, ratelimits, and timezones are sequences: each list element is a mapping that describes one object of that type.

modules

Each item must contain a load key (the module name). Additional keys supply module-level parameters:

modules:
  - load: imuxsock
  - load: imklog
  - load: imtcp
    threads: 2
  - load: imudp

inputs

Each item must contain a type key. All other keys are input instance parameters:

inputs:
  - type: imtcp
    port: "514"
    ruleset: main
  - type: imudp
    port: "514"
    ruleset: main
  - type: imfile
    file: "/var/log/app/*.log"
    tag: "applog"
    ruleset: main

templates

Four template types are supported:

String template — the most common type; a format string with %PROPERTY% placeholders:

templates:
  - name: myFormat
    type: string
    string: "%HOSTNAME% %syslogfacility-text%.%syslogseverity-text% %msg%\n"
  - name: filePerHost     # use with omfile dynafile: filePerHost
    type: string
    string: "/var/log/hosts/%HOSTNAME%.log"

Subtree template — serialises a JSON sub-tree of the message object:

templates:
  - name: jsonApp
    type: subtree
    subtree: "$!app"

List template — assembles output from an explicit sequence of property and constant items. Each element maps to a property() or constant() call; all parameters accepted by those RainerScript functions can be used:

templates:
  - name: listFmt
    type: list
    elements:
      - property:
          name: timestamp
          dateformat: rfc3339
      - constant:
          value: " "
      - property:
          name: hostname
      - constant:
          value: " "
      - property:
          name: msg
          droplastlf: "on"
      - constant:
          value: "\n"

Plugin template — delegates formatting to an external string-generator plugin (the type="plugin" form from RainerScript):

templates:
  - name: myPlugin
    type: plugin
    plugin: strgen_addprimary

rulesets

Ruleset items specify a name and may carry queue parameters. Rule logic is expressed in one of three ways:

  1. Structured shortcut (filter: + actions:) — for simple priority- or property-based routing without scripting. See Structured Filter Shortcut below.

  2. YAML-native statements (statements: key) — for conditional routing, if/then/else, call, call_indirect, foreach, stop, unset, variable assignment — without writing raw RainerScript blocks. See YAML-Native Statements below. Recommended for most YAML configs.

  3. Inline RainerScript (script: key) — intentional escape hatch for advanced logic that is better expressed directly in RainerScript. See Scripting below.

filter: + actions: example:

rulesets:
  - name: main
    filter: "*.*"
    actions:
      - type: omfile
        file: "/var/log/syslog"

  - name: errors
    filter: "*.err"
    actions:
      - type: omfile
        file: "/var/log/errors.log"
      - type: omfwd
        target: "logserver.example.com"
        port: "514"
        protocol: "tcp"

  # Property filter — starts with ':'
  - name: tag_filter
    filter: ":syslogtag, startswith, \"myapp\""
    actions:
      - type: omfile
        file: "/var/log/myapp.log"

  # No filter — unconditional routing
  - name: archive
    actions:
      - type: omfile
        file: "/var/log/archive.log"

script: example (complex logic):

rulesets:
  - name: errors
    script: |
      if $syslogseverity <= 3 then {
        action(type="omfile" file="/var/log/errors.log")
      }
      stop

parsers

Custom parser chains (loaded via lmregdups, pmrfc3164, etc.). Each item must contain a name key and the parser-specific parameters:

parsers:
  - name: pmrfc3164.default
    force.tagEndingByColon: "on"

lookup_tables

In-memory lookup tables loaded from JSON files. The name and filename keys are required.

lookup_tables:
  - name: hostmap
    filename: "/etc/rsyslog.d/hostmap.json"
    reloadOnHUP: "on"

timezones

Named timezone definitions for use in template date formatting:

timezones:
  - id: "CET"
    offset: "+01:00"

dyn_stats, perctile_stats, ratelimits

These advanced sections follow the same key=value mapping pattern. Refer to the respective module documentation for available parameters. Example:

dyn_stats:
  - name: "msg_rate"
    resetsIntervals: 60

ratelimits:
  - name: "input_rl"
    interval: 5
    burst: 1000

Arrays

Parameters that accept a list of values are expressed as YAML sequences:

global:
  environment:
    - "TZ=UTC"
    - "LC_ALL=C"

Including Other Files

The include: section accepts a sequence of items. Each item must have a path key (which may contain shell globs) and an optional optional flag:

include:
  - path: "/etc/rsyslog.d/*.yaml"
    optional: "on"
  - path: "/etc/rsyslog.d/*.conf"
    optional: "on"
  - path: "/etc/rsyslog.d/tls.conf"

If optional is on (or yes or 1) a missing file or empty glob result is not an error. Files with a .yaml or .yml extension are loaded by the YAML loader; all other files are loaded by the RainerScript parser.

Note

Include ordering with mixed file types: within one include: list, .yaml files are always loaded before .conf files, regardless of their order in the list. This is an architectural constraint — YAML sub-files are processed immediately and recursively, while .conf files are deferred to the RainerScript flex-buffer stack. If strict ordering between .yaml and .conf fragments matters, use separate include: sections or consolidate to one file type. Multiple .conf entries within the same list ARE processed in document order.

Structured Filter Shortcut

For common routing patterns (match by severity or message property, then write to one or more outputs) you do not need any RainerScript. Use filter: and actions: directly on a ruleset:

filter:

Optional string. Two forms are recognised:

  • Priority filter — standard syslog selector syntax (e.g. *.info, kern.warn, auth,authpriv.*).

  • Property filter — starts with : (e.g. :msg, contains, "error").

If filter: is absent, all messages match (unconditional routing).

actions:

YAML sequence. Each item maps directly to a action() call; the type key names the output module and all remaining keys are module-specific parameters. Multiple actions are chained in order.

rulesets:
  # Route *.info to syslog; *.err also to a dedicated error file
  - name: main
    filter: "*.info"
    actions:
      - type: omfile
        file: "/var/log/syslog"

  - name: errors
    filter: "*.err"
    actions:
      - type: omfile
        file: "/var/log/errors.log"
      - type: omfwd
        target: "logserver.example.com"
        port: "514"
        protocol: "tcp"

  # Property filter
  - name: myapp
    filter: ":syslogtag, startswith, \"myapp\""
    actions:
      - type: omfile
        file: "/var/log/myapp.log"

Note

filter: and actions: are mutually exclusive with script: and statements:. If you need conditionals, loops, set, call, or stop, use statements: (recommended) or script: instead.

YAML-Native Statements

The statements: key is the recommended way to express conditional routing and control flow for most YAML configs. Each item in the sequence is a YAML mapping that represents one statement. Only the filter expression inside if: remains as a RainerScript expression string. All structural elements and action parameters are expressed as YAML.

statements: is mutually exclusive with script:, filter:, and actions:.

If / action (single action shorthand)

rulesets:
  - name: main
    statements:
      - if: '$msg contains "error"'
        action:
          type: omfile
          file: "/var/log/errors.log"
        else:              # optional
          - stop: true

If / then / else (multiple statements per branch)

rulesets:
  - name: main
    statements:
      - if: '$syslogseverity <= 3'
        then:
          - type: omfile
            file: "/var/log/critical.log"
          - type: omfwd
            target: "logserver.example.com"
            port: "514"
            protocol: "tcp"
        else:
          - type: omfile
            file: "/var/log/messages"

Control flow: stop, continue, call

- stop: true        # stop processing this message
- continue: true    # continue (no-op, rarely needed)
- call: other_rs    # call another ruleset by name

Variable assignment

- set:
    var: "$.nbr"
    expr: 'field($msg, 58, 2)'

Supported statement types

Type

YAML form

Action

{type: module, param: val, ...} — unconditional

If/action

{if: expr, action: {type: ..., ...}, else: [...]}

If/then

{if: expr, then: [...], else: [...]}

Stop

{stop: true}

Continue

{continue: true}

Call

{call: rulesetname}

Set

{set: {var: "$.x", expr: "rainerscript-expression"}}

Unset

{unset: "$.x"}

call_indirect

{call_indirect: "$.varname"}

foreach

{foreach: {var: "$.i", in: "$!arr", do: [...]}}

reload_lookup_table

{reload_lookup_table: {table: name, stub_value: val}}

foreach iterates over a JSON array. The do: value is a sequence of statement items using the same syntax as statements:.

Complex routing and iteration example

This example demonstrates how to combine variable assignment, a foreach loop, and a nested if/then/else statement to achieve complex routing without writing raw RainerScript blocks.

rulesets:
  - name: process_items
    statements:
      # Set a local variable based on message content
      - set:
          var: "$.is_audit"
          expr: 're_match($msg, "AUDIT")'

      # Parse the JSON message content into $! variables
      - type: mmjsonparse

      # Iterate over a JSON array in the message
      - foreach:
          var: "$.item"
          in: "$!items"
          do:
            - if: '$.is_audit == 1'
              then:
                - type: omfile
                  file: /var/log/audit_items.log
                  template: outfmt
              else:
                - if: '$.item!type == "error"'
                  then:
                    - type: omfile
                      file: /var/log/error_items.log
                      template: outfmt
                  else:
                    - type: omfile
                      file: /var/log/standard_items.log
                      template: outfmt

Scripting

Use script: when a ruleset is better expressed directly in RainerScript, or when you need advanced logic that you intentionally want to keep in RainerScript form. For normal YAML configs, prefer statements: so the structure stays visible to YAML tooling, review, and generation workflows.

RainerScript filter expressions and statement types (if/then/else, set, unset, foreach, stop, call, legacy PRI-filters, property-filters, action()) are all available inside a script: block. The value is an ordinary YAML scalar (use a YAML block scalar for multi-line content):

rulesets:
  - name: main
    script: |
      # Route by severity
      if $syslogseverity <= 3 then {
        action(type="omfile" file="/var/log/critical.log")
        action(type="omfwd"  target="logserver.example.com" port="514"
                             protocol="tcp")
      }

      # Discard noisy debug messages
      if $syslogseverity == 7 then stop

      # Default sink
      action(type="omfile" file="/var/log/messages")

The script content is passed verbatim to the RainerScript lexer/parser, so anything that is valid RainerScript in a ruleset() {} body is valid here. Inline actions defined in the script: block do not need to be listed separately under an inputs: or actions: section.

Tip

For simple routing (one filter, one or more actions, no branching) use the filter: + actions: shortcut described in Structured Filter Shortcut. For conditional routing, variable assignments, call, foreach, stop, and reload_lookup_table use the statements: block described in YAML-Native Statements. Recommended for most YAML configs. Reserve script: for advanced logic that is better expressed directly in RainerScript, such as complex nested legacy priority/property filters or inline call_direct patterns.

Complete Example

The following is a near-complete rsyslog YAML configuration equivalent to a typical /etc/rsyslog.conf:

version: 2

global:
  workdirectory: "/var/spool/rsyslog"
  maxmessagesize: 8192
  privdrop.user.name: "syslog"
  privdrop.group.name: "adm"

modules:
  - load: imuxsock
  - load: imklog
  - load: imtcp
    threads: 2

inputs:
  - type: imtcp
    port: "514"
    ruleset: main
  - type: imuxsock

templates:
  - name: fileFormat
    type: string
    string: "%TIMESTAMP% %HOSTNAME% %syslogtag%%msg%\n"

mainqueue:
  type: linkedlist
  size: 100000

rulesets:
  - name: main
    filter: "auth,authpriv.*"
    actions:
      - type: omfile
        file: "/var/log/auth.log"

  - name: main_all
    filter: "*.*;auth,authpriv.none"
    actions:
      - type: omfile
        file: "/var/log/syslog"

  - name: kern
    filter: "kern.*"
    actions:
      - type: omfile
        file: "/var/log/kern.log"

  - name: emerg
    filter: "*.emerg"
    actions:
      - type: omusrmsg
        users: "*"

include:
  - path: "/etc/rsyslog.d/*.yaml"
    optional: "on"
  - path: "/etc/rsyslog.d/*.conf"
    optional: "on"

Relationship to RainerScript

YAML configuration is a thin translation front-end over the same internal machinery that RainerScript uses. The YAML parser (runtime/yamlconf.c) converts each YAML block into cnfobj + nvlst structures — the identical intermediate representation that the RainerScript lex/bison grammar produces — and hands them to the shared cnfDoObj() dispatcher. There is no independent YAML runtime; the shared back-end handles all validation, module initialisation, and execution.

That shared back-end is why RainerScript experience transfers directly. Whether you write YAML or RainerScript, you are still configuring the same rsyslog objects and execution model.

This approach deliberately minimises the change surface: the YAML-specific code is confined to one file with no runtime presence after configuration loading. It may be refactored in the future, but only when concrete requirements justify the additional maintenance surface.

In practical terms:

  • Parameter names are identical; all per-module parameter documentation applies without change.

  • Type coercion (integers, sizes, binary flags, UIDs, …) is performed by the same nvlstGetParams() layer after YAML parsing.

  • Unknown parameter names produce the same “unused parameter” error regardless of which format was used.

  • Template, ruleset, and lookup-table names are shared; a YAML-defined ruleset can be referenced from a RainerScript action().

For a detailed description of the pipeline see yaml_config_architecture.

Limitations (current implementation)

  • Nested YAML mappings inside parameter values are not supported. Use a YAML scalar or sequence.

  • filter: + actions: support one flat filter level. For nested if/then/else chains use statements: (recommended) or script:.

  • version: is accepted but not enforced; it is reserved for future schema evolution.

  • Within a single include: list, .yaml files are always processed before .conf files regardless of document order (see Including Other Files).

See Also


Support: rsyslog Assistant | GitHub Discussions | GitHub Issues: rsyslog source project

Contributing: Source & docs: rsyslog source project

© 2008–2026 Rainer Gerhards and others. Licensed under the Apache License 2.0.