.. _yaml_config:

YAML Configuration Format
=========================

.. meta::
   :description: rsyslog YAML configuration format reference: schema, activation, ruleset scripting, templates, and relationship to RainerScript.
   :keywords: yaml, configuration, rsyslog, rainerscript, yamlconf, yaml config

.. summary-start

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.

.. summary-end

rsyslog supports configuration via YAML files as an additional format alongside
RainerScript. It is one of the :doc:`supported configuration formats
<conf_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
:doc:`../tutorials/config_format_translation` 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:

.. code-block:: shell

   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
   <https://www.rsyslog.com>`_.  The easiest option is often a
   containerized build: the rsyslog team publishes ready-to-use images
   documented in :doc:`../containers/index`.

   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:

.. code-block:: yaml

   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.

.. code-block:: yaml

   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
:doc:`global configuration parameters <global/index>` and
:doc:`queue parameters <../rainerscript/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:

.. code-block:: yaml

   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:

.. code-block:: yaml

   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:

.. code-block:: yaml

   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:

.. code-block:: yaml

   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:

.. code-block:: yaml

   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):

.. code-block:: yaml

   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:

.. code-block:: yaml

   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):

.. code-block:: yaml

   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:

.. code-block:: yaml

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

**lookup_tables**

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

.. code-block:: yaml

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

**timezones**

Named timezone definitions for use in template date formatting:

.. code-block:: yaml

   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 :doc:`module documentation <../index>` for
available parameters.  Example:

.. code-block:: yaml

   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:

.. code-block:: yaml

   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:

.. code-block:: yaml

   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.

.. _yaml_filter_shortcut:

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.

.. code-block:: yaml

   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_statements:

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)**

.. code-block:: yaml

   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)**

.. code-block:: yaml

   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**

.. code-block:: yaml

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

**Variable assignment**

.. code-block:: yaml

         - 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.

.. code-block:: yaml

   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

.. _yaml_scripting:

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 <https://yaml.org/spec/1.2.2/#81-block-scalar-styles>`_
for multi-line content):

.. code-block:: yaml

   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``:

.. code-block:: yaml

   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
:doc:`yaml_config_architecture <../development/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
--------

- :doc:`Configuration formats overview <conf_formats>`
- :doc:`RainerScript reference <../rainerscript/index>`
- :doc:`Basic configuration structure <basic_structure>`
- :doc:`Converting legacy config <converting_to_new_format>`
- :doc:`Global configuration parameters <global/index>`
- :doc:`Queue parameters <../rainerscript/queue_parameters>`
