1. Introduction
In the commercial editions you can use the Bakery API to write your own, so-called Bakery plug-ins, which include functions in the agent packages from the Agent Bakery.
In most cases, these functions are agent plug-ins, i.e. additional scripts to be executed by the Checkmk agent, and the plug-ins' configuration files.
However, they can also affect the package manager functions if they can be mapped by including files, running package scriptlets (for RPM, DEB and Solaris PKG package formats), or if they define specific configuration entries for the Windows agent (in YAML).
All of these 'artifacts' can be described in the Bakery API using a uniform syntax.
For example, one real-world application scenario is as follows: You read the introduction to developing extensions for Checkmk and, inspired by it, wrote your own agent-based check plug-in with its associated agent plug-in. You then combined these two into a single Checkmk extension package (MKP).
Now you want to make the agent plug-in configurable (e.g., enable it to be run only by certain users or on certain hosts) and additionally perform actions when installing or uninstalling the agent package. To do this, you can use the Bakery API — as a packaging and distribution aid, as we will show in this article with an example scenario. This creates two new files, which can then be packaged together with the existing plug-in files to create a new MKP. You can also find a thoroughly commented example of this procedure in the Checkmk Exchange: the Hello world! MKP (unpacked on GitHub), which is closely based on the example scenario presented in this article.
Note: The Bakery API does not provide functions for configuring the Bakery plug-in, i.e. for creating the associated rule set, nor for the contents of the files provided with the plug-in, e.g. the agent plug-ins.
![]() |
Even though the Agent Bakery is only included in the commercial editions, the Bakery API exists in all editions since Checkmk 2.3.0. This allows Checkmk Raw users to create extension packages that can be installed on all editions. If packages created with the Bakery API are installed on a Checkmk Raw, the additional functionality is simply ignored. |
2. The API documentation
2.1. Versioning
Bakery API software and documentation come from the same source, so the API documentation always matches the software and describes exactly what the API can do, and it is therefore not necessary to describe the reference part of the available functions, classes, parameters, etc. in the Checkmk User guide. Instead, you can find the API documentation outside this User guide, directly in its Checkmk site.
The API with its documentation is versioned using two-level numbering conforming to the Semantic Versioning 2.x standard in the format X.Y
, where X
stands for a major version and Y
for a minor version.
A new minor version contains new, backward compatible features.
A new major version, on the other hand, may contain changes that make the API incompatible with the previous major version.
Version 1
is the current version of the Bakery API described in this article.
Each plug-in explicitly declares the API version it is based on at the access to the API.
The API follows a different versioning scheme to the Checkmk software. Nevertheless, mapping the versions of API documentation and Checkmk software is very simple, as you will learn in the next chapter.
2.2. Access to the API documentation
The Bakery API documentation is available in HTML format for viewing in a web browser and can be opened from the Checkmk GUI: from the navigation bar in the Help > Developer resources > Plug-in API references menu:

The plug-in API documentation is displayed in a new browser window (or browser tab):

This window displays API documentation relevant to the development of Checkmk plug-ins, i.e. here you will find the documentation for the Check API in addition to the Bakery API documentation. The API documentation is generated and displayed with Sphinx.
You can find the Bakery API documentation in the versions supported by the Checkmk version installed in your site.
3. Using the API
3.1. A sample scenario
We will demonstrate the use of the API with the following sample scenario:
A plug-in with the name
hello_world
is provided for the Checkmk agent.The agent plug-in exists in three variants — for Linux, Solaris and Windows — and is to be included in the agent packages for these three operating systems as well.The corresponding files are also available and are called
hello_world
(for Linux),hello_world.solaris.ksh
(for Solaris) andhello_world.cmd
(for Windows).
The Python, shell and CMD scripts are only examples. An agent plug-in can be any file executable on the target system.
We are not interested in the actual content of the files in this context. The function of agent plug-ins is not the subject of the Bakery API. You can learn more about this in the introduction to developing your own agent-based check plug-ins.It should be configurable whether the plug-in’s output should be cached, i.e. in this case the plug-in will only be executed again by the agent once the configured time (execution interval) has elapsed.
The plug-in is to be configured in the Setup menu via Agent Bakery settings with the variables
user
andcontent
. The Linux plug-in reads the configuration from thehello_world.json
configuration file, and the Solaris plug-in reads it from thehello_world.cfg
file. The Windows plug-in reads thehello_world.user
andhello_world.content
entries from the YAML configuration file in the Windows agent.
In each case, access to these resources must be implemented in the agent plug-in and is not handled by the Bakery API.For Linux and Solaris there is an additional program
some_binary
, which should be delivered e.g. a small shell script, to also be able to start the plug-in by a command independently of the Checkmk agent.Under Linux and Solaris, after installing the agent, it should be written to the syslog via a package manager routine that
hello_world
has been installed. After uninstalling the agent, analogously, it should be written to syslog thathello_world
has been uninstalled.
Common under Linux arepostinst
andprerm
scripts: in thepostinst
script you create for example a cache and start a daemon, in theprerm
script you can then stop the daemon again and clear the cache. For more information on how to usemaintainer scripts
, see the Debian documentation.
3.2. Creating a rule set
For a Bakery plug-in there must be a set of rules for the Setup, with which the plug-in can be configured via the GUI. In the simplest case the rule set only activates a plugin by assigning it to certain hosts. The creation of a rule set is not part of the Bakery API. You can find an introduction to this topic in the developing your own agent-based check plug-ins article. There, also path conventions for storing rule sets are explained.
Minimal rule set
An example of such a minimal rule set, which only activates the distribution of a plugin, could look as follows.
The imported classes here are more extensive than required in order to also cover the extended example.
The most noticeable difference to the rule set for agent-based check plug-ins is the use of the class AgentConfig
instead of CheckParameters
.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Shebang only needed for editor!
from cmk.rulesets.v1 import Label, Title, Help
from cmk.rulesets.v1.form_specs import (
BooleanChoice,
DefaultValue,
DictElement,
Dictionary,
String,
TimeSpan,
TimeMagnitude
)
from cmk.rulesets.v1.rule_specs import AgentConfig, HostCondition, Topic
def _parameter_form_bakery():
return Dictionary(
elements = {}
)
rule_spec_hello_world_bakery = AgentConfig(
name = "hello_world",
title = Title("Hello bakery!"),
topic = Topic.GENERAL,
parameter_form = _parameter_form_bakery,
)
Extended rule set
However, in the example scenario, we have specified that an execution interval should be chosen and the two variables user
and content
should be set.
These variables are defined as keys in the Dictionary
, which is returned by the function specified as parameter_form
.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Shebang only needed for editor!
from cmk.rulesets.v1 import Label, Title, Help
from cmk.rulesets.v1.form_specs import (
BooleanChoice,
DefaultValue,
DictElement,
Dictionary,
String,
TimeSpan,
TimeMagnitude
)
from cmk.rulesets.v1.rule_specs import AgentConfig, HostCondition, Topic
def _parameter_form_bakery():
return Dictionary(
elements = {
"user": DictElement(
parameter_form = String(
title = Title("User for example plugin"),
)
),
"content": DictElement(
parameter_form = String(
title = Title("The actual content"),
)
),
"interval": DictElement(
parameter_form = TimeSpan(
title = Title("Run asynchronously"),
label = Label("Interval for collecting data"),
displayed_magnitudes = [TimeMagnitude.SECOND, TimeMagnitude.MINUTE],
prefill = DefaultValue(300.0),
)
)
}
)
rule_spec_hello_world_bakery = AgentConfig(
name = "hello_world",
title = Title("Hello bakery!"),
topic = Topic.GENERAL,
parameter_form = _parameter_form_bakery,
)
The GUI resulting from this rule set is shown in the following screenshot:

3.3. Creating a plug-in file
The hello_world.py
plug-in file is stored in the local part of the site directory structure at local/lib/check_mk/base/cee/plugins/bakery/
.
A Bakery plug-in is created in the form of a file that is imported as a Python 3 module. Therefore, following Checkmk convention, plug-in files also start with the following lines:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
Since this is a module, all required classes and functions must be imported at the beginning.
3.4. Accessing the API
All objects in the Bakery API are available under cmk.base.cee.plugins.bakery.bakery_api.vX
, where X denotes the API version number, in the example 1
.
Since the plug-in file itself is located in the cmk.base.cee.plugins.bakery
namespace, a relative import from .bakery_api.v1
will also work:
from .bakery_api.v1 import (
OS,
DebStep,
RpmStep,
SolStep,
Plugin,
PluginConfig,
SystemBinary,
Scriptlet,
WindowsConfigEntry,
register,
FileGenerator,
ScriptletGenerator,
WindowsConfigGenerator,
quote_shell_string,
)
In the above example, only the names that are needed for the example scenario are imported.
3.5. The objects available in the API
The names available in the Bakery API are described in detail in the API documentation. In this chapter, the objects are nevertheless briefly introduced, as this is helpful for being able to understand the implementation of the example scenario.
Identifier / Enumerations
For the specification of the individual plug-in artifacts, enumerations (Enum
) are available, which can be used to specify various properties, usually in the form of an argument:
OS
- The operating system in the context of the Bakery API.DebStep
- A transaction step for a DEB 'maintainer script'RpmStep
- A transaction step for an RPM 'scriptlet'.SolStep
- A transaction step for a Solaris PKG 'installation script'.
Artifacts
The files and file contents that are the actual components of a plug-in are referred to as artifacts. These are described using appropriate classes, which can be divided into the following categories:
Files (
Plugin
,SystemBinary
,PluginConfig
,SystemConfig
) - Each file to be provided to the Checkmk agent is described with an object. The file type is described by the class. A separate object must be defined for each operating system on which the file is to be deployed.Scriptlets (
Scriptlet
) - A DEB 'maintainer script', RPM 'scriptlet' or Solaris PKG 'installation script' to be executed when installing, uninstalling or updating the agent package at the specified transaction step (e.g.preinstall
,postremove
).Windows configuration entries (
WindowsConfigEntry
,WindowsConfigItems
,WindowsGlobalConfigEntry
,WindowsSystemConfigEntry
) - Entries in the YAML configuration file for the Windows agent are also described using appropriate classes.
These artifacts are each described in callback functions corresponding to their category.
The individual functions are passed to the register function with the arguments files_function
, scriptlets_function
, windows_config_function
.
These are generator functions that return the individual specified artifacts.
The evaluation is done by the Agent Bakery.
The functions receive various parameters as arguments that can be evaluated in order to construct and determine the returned artifacts.
The parameters are on the one hand the GUI configuration of the respective agent that is to be baked (conf
), and on the other hand the hash of the current agent configuration and plug-in files (aghash
).
The register function
The registration is performed with the register
function, which is called when importing the Bakery plug-in as a module.
The function receives the individual components of the Bakery plug-in as arguments:
the plug-in’s name (name
) and its functions (files_function
, scriptlets_function
, windows_config_function
), each of which returns a category of artifact.
Type annotations
Names for type annotations (FileGenerator
, ScriptletGenerator
, WindowsConfigGenerator
, WindowsConfigContent
) can optionally be used to identify the type of the function specified, e.g. like this:
def get_files(conf: dict) -> FileGenerator:
yield Plugin(...)
yield PluginConfig(...)
def get_scriptlets(conf: dict) -> ScriptletGenerator:
yield Scriptlet(...)
def get_windows_config(conf: dict) -> WindowsConfigGenerator:
content: WindowsConfigContent = conf["some_entry"]
yield WindowsGlobalConfigEntry(name="some_name",content=content)
Helper functions
The following helper functions can be used:
quote_shell_string
- This function can be used to convert a string expression so that it is correctly recognized as an expression by the shell in the resulting file — without having to manually mask the quotes in the Python code.password_store
- This module allows access to passwords stored in the Checkmk password store.
3.6. Registration
Registering the plug-in with Checkmk with a plug-in name and its functions is accomplished with the register.bakery_plugin
function:
register.bakery_plugin(
name = "hello_world",
files_function = get_hello_world_plugin_files,
scriptlets_function = get_hello_world_scriptlets,
windows_config_function = get_hello_world_windows_config,
)
The get_hello_world_windows_config
, get_hello_world_scriptlets
and get_hello_world_plugin_files
functions specified here are explained in more detail in the following chapters.
3.7. Configuration for the Windows agent
In our example, the interval for execution must be be defined, and the configuration of the plug-in should be able to be done via two variables. Make sure to define keys and data types as defined in the rule set created above.
class HelloWorldConfig(TypedDict, total=False):
interval: float
user: str
content: str
def get_hello_world_windows_config(conf: HelloWorldConfig) -> WindowsConfigGenerator:
yield WindowsConfigEntry(path=["hello_world", "user"], content=conf["user"])
yield WindowsConfigEntry(path=["hello_world", "content"], content=conf["content"])
In the get_hello_world_windows_config
function, we use the conf
argument to access the configuration set via the rule set in the Setup GUI:
The time interval for re-execution on cached output (interval
) and the two variables that can be used to configure the plug-in (user
, content
).
Here we assume that the rule set configuration is supplied as a dict
. Using the TypedDict
of the class HelloWorldConfig
we can set up a standardized access to it.
Then WindowsConfigEntry
is used to specify the entries in the YAML-Windows agent configuration file from which the values for user
and content
are read.
3.8. Installation script for Linux
On Linux and Solaris, syslog messages should be written when installing and uninstalling the agent. Here we show only an implementation of the Debian Linux distribution:
def get_hello_world_scriptlets(conf: Any) -> ScriptletGenerator:
installed_lines = ['logger "Installed hello_world"']
uninstalled_lines = ['logger "Uninstalled hello_world"']
yield Scriptlet(step=DebStep.POSTINST, lines=installed_lines)
yield Scriptlet(step=DebStep.POSTRM, lines=uninstalled_lines)
# yield Scriptlet(step=RpmStep.POST, lines=installed_lines)
# yield Scriptlet(step=RpmStep.POSTUN, lines=uninstalled_lines)
# yield Scriptlet(step=SolStep.POSTINSTALL, lines=installed_lines)
# yield Scriptlet(step=SolStep.POSTREMOVE, lines=uninstalled_lines)
First the commands for the syslog messages are defined and then the installation scripts for Debian (DebStep
) which should be executed after the installation (POSTINST
) and after the uninstallation (POSTRM
).
In the comments below you will also find the corresponding lines for distributions that use RPM and for Solaris.
Note: Following the command lines that you have included, the installation scripts are loaded with additional commands by Checkmk.
Therefore, to ensure that all of the commands in the scripts are executed, do not end your command set with an exit 0
.
3.9. The agent plug-in for Linux
The configuration for the Linux agent plug-in looks like this.
Make sure to convert the interval passed to Integer
since rule set and bakery use different data types.
def get_hello_world_plugin_files(conf: HelloWorldConfig) -> FileGenerator:
interval = conf.get('interval')
yield Plugin(
base_os = OS.LINUX,
source = Path('hello_world'),
target = Path('hello_world'),
interval = int(interval),
)
yield PluginConfig(
base_os = OS.LINUX,
lines = _get_linux_cfg_lines(conf['user'], conf['content']),
target = Path('hello_world.json'),
include_header = False)
for base_os in [OS.LINUX]:
yield SystemBinary(
base_os = base_os,
source = Path('some_binary'),
)
def _get_linux_cfg_lines(user: str, content: str) -> List[str]:
config = json.dumps({'user': user, 'content': content})
return config.split('\n')
In the get_hello_world_plugin_files
function, first the Python file hello_world
is defined as a plugin
, i.e. as an executable file to be run by the Checkmk agent as an agent plug-in.
Then PluginConfig
is used to specify the hello_world.json
configuration file to be generated for the Linux agent plug-in with the user
and content
entries.
With the second function _get_linux_cfg_lines
these lines are written in JSON format.
Here, the Python dictionary conf
contains the values set with the rule set of the Setup GUI, which are then packed into a JSON file via a small detour.
Finally, the additional shell script some_binary
to be delivered is to be placed as SystemBinary
on the target system in the directory for user programs (by default /usr/bin
).
3.10. The plug-in file for our example scenario
Putting the all of the parts presented so far together — and completing them — a plug-in for our example scenario might look like this when finished:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Shebang only needed for editor!
import json
from pathlib import Path
from typing import Iterable, TypedDict, List
from .bakery_api.v1 import (
OS,
DebStep,
RpmStep,
SolStep,
Plugin,
PluginConfig,
SystemBinary,
Scriptlet,
WindowsConfigEntry,
register,
FileGenerator,
ScriptletGenerator,
WindowsConfigGenerator,
quote_shell_string,
)
class HelloWorldConfig(TypedDict, total=False):
interval: float
user: str
content: str
def get_hello_world_plugin_files(conf: HelloWorldConfig) -> FileGenerator:
interval = conf.get('interval')
yield Plugin(
base_os = OS.LINUX,
source = Path('hello_world'),
target = Path('hello_world'),
interval = int(interval),
)
yield Plugin(
base_os =OS.SOLARIS,
source = Path('hello_world.solaris.ksh'),
target = Path('hello_world'),
interval = int(interval),
)
yield Plugin(
base_os = OS.WINDOWS,
source = Path('hello_world.cmd'),
target = Path('hello_world.bat'),
interval = int(interval),
)
yield PluginConfig(
base_os = OS.LINUX,
lines = _get_linux_cfg_lines(conf['user'], conf['content']),
target = Path('hello_world.json'),
include_header = False
)
yield PluginConfig(
base_os = OS.SOLARIS,
lines = _get_solaris_cfg_lines(conf['user'], conf['content']),
target = Path('hello_world.cfg'),
include_header = True
)
for base_os in [OS.LINUX, OS.SOLARIS]:
yield SystemBinary(
base_os = base_os,
source = Path('some_binary'),
)
def _get_linux_cfg_lines(user: str, content: str) -> List[str]:
config = json.dumps({'user': user, 'content': content})
return config.split('\n')
def _get_solaris_cfg_lines(user: str, content: str) -> List[str]:
# To be loaded with 'source' in Solaris shell script
return [
f'USER={quote_shell_string(user)}',
f'CONTENT={quote_shell_string(user)}',
]
def get_hello_world_scriptlets(conf: HelloWorldConfig) -> ScriptletGenerator:
installed_lines = ['logger -p local3.info "Installed hello_world"']
uninstalled_lines = ['logger -p local3.info "Uninstalled hello_world"']
yield Scriptlet(step=DebStep.POSTINST, lines=installed_lines)
yield Scriptlet(step=DebStep.POSTRM, lines=uninstalled_lines)
yield Scriptlet(step=RpmStep.POST, lines=installed_lines)
yield Scriptlet(step=RpmStep.POSTUN, lines=uninstalled_lines)
yield Scriptlet(step=SolStep.POSTINSTALL, lines=installed_lines)
yield Scriptlet(step=SolStep.POSTREMOVE, lines=uninstalled_lines)
def get_hello_world_windows_config(conf: HelloWorldConfig) -> WindowsConfigGenerator:
yield WindowsConfigEntry(path=["hello_world", "user"], content=conf["user"])
yield WindowsConfigEntry(path=["hello_world", "content"], content=conf["content"])
register.bakery_plugin(
name = "hello_world",
files_function = get_hello_world_plugin_files,
scriptlets_function = get_hello_world_scriptlets,
windows_config_function = get_hello_world_windows_config,
)
3.11. Making the files available
For a Bakery plug-in to work properly, all of the files involved must be placed or written in the correct location in the site directory’s local structure.
These are on the one hand the plug-in file itself and on the other hand the objects returned by the files_function
. These objects either describe configuration files that are created directly by the Bakery plug-in, or they refer to files that must be stored correctly so that they can be found when packaging the agent packages.
Objects of the Plugin
and SystemBinary
classes denote existing files that must be stored.
The files described as PluginConfig
and SystemConfig
are yet to be generated based on the lines
argument, so no files need to be stored here.
Finally, the set of files also includes the rule set file for the plug-in.
In the next and last chapter you will find the compilation of all directories.
4. Files and directories
Files for deploying a Bakery plug-in must be placed in the following directories.
As always, all specifications here are relative to the site’s directory (e.g. /omd/sites/mysite
).
File path | Description |
---|---|
|
Directory for the Bakery plug-in (in our example |
|
Directory for storing the Unix-like agent plug-ins. |
|
Directory for storing the Windows agent plug-ins. |
|
Directory for included programs or shell scripts for Unix-like operating systems ( |
|
Directory for supplied programs or shell scripts for Windows. |
|
Directory for the rule set files for configuring the agent plug-in (in the example |