Skip to content

Configuration

Chris edited this page Nov 14, 2026 · 1 revision

Configuration

The bot reads /config/config.toml inside its container. Compose mounts your config.toml at that path, read-only. To read a different path, set the SHAZZZBOT_CONFIG environment variable.

The file uses top-level keys and named tables, for example [history] and [[outputs]]. Put every top-level key above the first [table] line. In TOML, a line after a table header belongs to that table. Thus a key under [tasks.rename] is a task name. When a [tasks] key is there by mistake, the bot stops with ['shown'] sit under [tasks.rename]; move them above it, into [tasks].

The bot checks the file at start. If a setting is unknown, missing, or not valid, the bot stops and the log names the setting, for example unknown setting 'intervall'; did you mean 'interval'?. Secrets never go in this file. They go in .env (see Environment variables).

config.example.toml lists every key with its default and a one-line comment. The preview site has a settings editor that checks a file with the same rules as the bot.

Top-level settings

Key Type Default Description
config_format integer none, required The format of the file. The only value is 1. A file in an older format stops the bot with run setup.sh --action migrate. A file from a newer version also stops the bot.
guild_id integer none, required The id of your Discord server.
audit_channel_id integer none The id of the audit channel. 0 or no value means no audit channel, and then all changes are off.
commands_channel_id integer none One more channel where the bot answers slash commands. 0 or no value means none.
blowup_user_id integer 0 The user that /blowup pings. 0 turns /blowup off.
interval integer 10 Seconds between host readings. The minimum is 5.
edit_every integer 60 Seconds between edits of a message without images. The minimum is 5. An [[outputs]] block can set its own value.
image_every integer 60 Seconds between image uploads of a message with images. The minimum is 60. An [[outputs]] block can set its own value.
state_file string "/data/state.json" The file that keeps the message ids, the chart time span, the task run times, and the learned upload budget across restarts.
timezone string "UTC" An IANA time zone name, for example "America/New_York". The bot uses it for the time stamps on the images, the next run of each task, and the clock times of edits and uploads.
green_ok boolean false true shows a green dot on rows with no problem. false shows a dark square.
order list of section names all sections, in the order of Sections The order of the sections, top to bottom, for example ["system", "services"]. Each name can appear once. Sections that are not in the list follow in the default order.
[[outputs]] array of tables none, required One block for each status channel. See Outputs.

Keep state_file at its default. It must be in the /data volume that compose.yaml mounts. The chart history (history.db), the Top Apps history (apps.json), and the generated preview login (preview-login.json) go in the same directory.

The other keys are tables. Each one has its own section on this page:

Outputs

An output is the status panel in one channel. Add one [[outputs]] block for each channel. You must have at least one block. The first output is also the one that /status shows.

[[outputs]]
channel = 123456789012345678
[outputs.render]
docker = "text"

[[outputs]]
channel = 234567890123456789
sections = ["system", "history"]
image_every = 1800
edit_every = 120
Key Type Default Description
channel integer none, required The channel id. Each output needs a different channel.
sections list of section names all sections The sections to show, at least one, each name once. They appear in the order setting order, not in the order of this list.
image_every integer the top-level image_every Seconds between image uploads in this channel. The minimum is 60.
edit_every integer the top-level edit_every Seconds between edits in this channel. The minimum is 5.
[outputs.render] table {} A mode for each section in this channel. It overrides the top-level [render] for the sections that it names.

The panel of one output can use more than one message. A Discord message holds at most 10 embeds and 6000 characters of embed text. The bot puts each section in one embed and packs the sections in order into as few messages as possible. A section that is too long for one message gets cut, and its last row shows ...and N more. When the panel needs fewer messages, the bot deletes its extra messages.

Sections

Name Title Contents
system the [system] title Host name, Unraid version, uptime, CPU, RAM, CPU temperature, load, disk I/O, network I/O, array use, the UPS, alerts, tasks, Jellyfin streams, and the node status. In image mode, the disk usage bars follow the System summary in the same image.
history History CPU, RAM, disk I/O, and network I/O charts. In image mode, they are an image of their own.
disks Disks The array state, and the temperature, used and free space, read and write rates, busy percent, errors, and SMART health of each disk.
services Services The results of the service checks. Checks with no container go under an External Checks heading.
docker Docker Containers The engine version, the number of containers that run, and the state of each Compose stack.
apps Top Apps The ten busiest containers, VMs, and processes by CPU and by RAM, each with a trend line.
tasks Tasks The tasks that run now, with the elapsed time, and the tasks that run next. It also shows parity checks and the mover.
forges the forge name One section for each [[forges]] block.

The Disks section needs the Unraid emhttp files. The Docker section needs [host] docker_proxy. The Services section appears only when there are service checks or a relay.

The Top Apps trend lines cover the chart time span that /charts window sets. The bot keeps the history of each app in apps.json, so the trend lines survive a restart. A (+) after a name shows that the row adds up more than one instance of that app.

Render modes

The top-level [render] table sets a mode for each section. A section that the table does not name uses "image". [outputs.render] changes the mode for one output.

[render]
tasks = "embed"

[[outputs]]
channel = 123456789012345678
[outputs.render]
system = "text"

The keys are the section names: system, history, disks, services, docker, apps, tasks, and forges. Each value is one of these modes:

  • "image": a PNG image. This is the default for every section.
  • "text": a monospace table that fits a phone screen.
  • "embed": Discord embed fields.

The Mixed preset on the preview site sets tasks = "embed" and "image" for all other sections.

Edit and image schedule

A message with no image sections changes at each multiple of edit_every seconds on the clock. The bot skips the edit when nothing in the message changed.

A message that holds images changes only when its images upload. The bot sends the text and the images of that message in one request, on the image_every schedule. Because "image" is the default mode, a panel with the default [render] changes on the image_every schedule. A section in "text" or "embed" mode changes on the edit_every schedule only when its message holds no images.

Important

Discord limits the image uploads in one server and does not publish the limit. In a test on 2026-11-14, one upload every 75 seconds (48 an hour) held, and refusals began near 65 uploads in an hour, counted across all channels. Uploads by other bots in the server appear to count too. Thus the bot uploads at most one image set every 75 seconds, for all outputs together. The bot learns the limit from the Discord replies, and that budget can delay an upload more. When Discord refuses an upload, the message keeps its old content and the bot tries again later.

/panel refresh and a change of /charts window ask for new images at once, within the same upload limit.

History

Key Type Default Description
days integer 7 Days of chart history to keep in the data volume, from 1 to 7. It is also the longest span that /charts window and /chartgen accept.

System

Key Type Default Description
title string "Unraid System Info" The title of the System section.
subtitle string "" Text under the title. Empty shows no text.

Host

Key Type Default Description
root string "/host" The directory in the container that holds the host proc and sys mounts.
emhttp string "/host/emhttp" The directory in the container that holds the Unraid emhttp state files.
docker_proxy string none The URL of the Docker proxy. config.example.toml sets "http://docker-proxy:2375". With no value, the bot does not read Docker, the Docker section is gone, and the /docker commands answer Docker is not configured.

Keep root and emhttp at their defaults. They must agree with the mounts in compose.yaml.

Thresholds

Each threshold in the [thresholds] table is a pair of numbers, [warning, critical], and the warning value must not be more than the critical value. A single number sets a warning level with no critical level. A value at or above a limit gets that level. Omit a threshold to keep its default.

Threshold Default Applies to
resource [80, 90] CPU percent, RAM percent, and network percent of link speed. It also sets the bar colors of disk I/O and UPS charge.
load [0.7, 1.0] The load average divided by the number of CPU cores.
iowait [13, 24] The percent of time that the CPU waits for disk I/O.
temp [80, 95] CPU temperature in degrees Celsius.
disk_full [90, 95] The used-space percent of each disk and of the whole array.
hdd_temp [45, 55] Hard disk temperature in degrees Celsius.
ssd_temp [60, 70] SSD and NVMe temperature in degrees Celsius.
disk_busy 95 The busy percent of each disk and of the busiest disk. The default is a warning only. Use [95, 99] to add a critical level.
[thresholds]
load = [0.7, 1.0]
disk_busy = [95, 99]

Permissions

The [perms] table holds three tier tables: [perms.view], [perms.operate], and [perms.admin]. Each tier table accepts these keys:

Key Type Default Description
roles list of integers [] Discord role ids that get the tier.
users list of integers [] Discord user ids that get the tier.
[perms.operate]
roles = [123456789012345678]
users = []

A higher tier includes the lower tiers. A user or role that no tier lists has no access. See Slash commands for what each tier allows.

Restarts

Key Type Default Description
allow list of strings [] Container names that the /docker commands can restart, as glob patterns, for example ["jellyfin", "*arr"]. The match is case-sensitive. An empty list turns restart off.
[restarts.groups] table {} Named groups of container globs for /docker restart-group.

A restart group is a name for a set of containers. /docker restart-group restarts all containers in the group.

[restarts]
allow = ["jellyfin*", "reader"]

[restarts.groups]
media = ["jellyfin*", "reader"]

Each group value is a list of one or more container globs. Each container that a group selects must also match allow. If one does not match, the command restarts nothing.

Tasks

The [tasks] table controls the Tasks section.

Key Type Default Description
shown integer 20 The maximum number of upcoming tasks in the Tasks section. The minimum is 1.
ignore list of strings [] Task names to hide, as glob patterns, for example ["boot-fim", "backup-*"]. A pattern matches the raw name or the display name.
long_running_minutes number or false 60 A running task shows in yellow when it runs longer than this, or when its last run took longer. 0 or false turns this check off.
heavy_kinds list of kinds all four kinds Kinds of task that always show in yellow while they run. Each kind can appear once. An empty list turns this check off.
[tasks.rename] table {} Maps a raw task name to a display name.

The yellow color does not add an alert. The kinds are these:

  • parity: a parity check.
  • file_integrity: the File Integrity plugin.
  • appdata_backup_plugin: the Appdata Backup plugin.
  • appdata_backup_script: a User Script that starts an appdata backup.
[tasks]
shown = 20
long_running_minutes = 60
heavy_kinds = ["parity", "file_integrity"]

[tasks.rename]
"backup-appdata.sh" = "Appdata backup"

The Tasks section needs the Host helper. Without it, the section shows needs the shazzzbot helper. Watchtower schedules, read from the container command line, appear in the upcoming list without the helper.

Services

The [services] table holds the service checks.

Key Type Default Description
columns list of 3 strings ["Local", "VPN", "Public"] The labels of the three stage columns in the Services table. No label can be empty.
ignore list of strings [] Check names to skip, as glob patterns. The bot does not check or show a skipped service.
public_host string "" The public host name or IP address of the server. With it, the bot lists the published ports that the internet can open. It needs a relay. See Relay.
[[services.checks]] array of tables none One block for each service check.

Each [[services.checks]] block accepts these keys:

Key Type Default Description
name string none, required The name in the Services table.
container string "" The Docker container name. Omit it for an external check.
url string "" The Local stage: an http:// or https:// URL.
tcp string "" The Local stage: a TCP target, host:port or [IPv6]:port.
vpn_url string "" The VPN stage: an http:// or https:// URL.
vpn_tcp string "" The VPN stage: a TCP target.
public_url string "" The Public stage: an http:// or https:// URL. A relay does the check when one is set.
public_tcp string "" The Public stage: a TCP target. A relay does the check when one is set.
expect string "" Text that the HTTP reply must contain.
every integer 60, the global cycle Seconds between checks of this service. The minimum is 1 with a container, and 60 without one.

Each block needs a container, a URL, or a TCP target. Each stage takes a URL or a TCP target, not both. The Service checks page explains the stages and the table values.

UPS

Key Type Default Description
target string "" A UPS on a NUT server (Network UPS Tools), as name@host or name@host:port. Empty means no UPS.
[ups]
target = "ups@192.0.2.20:3493"

The default port is 3493. Put an IPv6 address in brackets, for example ups@[2001:db8::1]. The bot does not log in to NUT and only reads values.

The System section then shows a UPS row with the charge, the runtime that is left, and the status. The row is critical for Low battery, Replace battery, Forced shutdown, Off, and Overloaded. It is a warning for On battery, Alarm, Bypass, Waiting for data, an unknown status, or no data.

With the Host helper and its ups_shutdown action, the row also shows the shutdown setting of the Unraid NUT plugin, for example (off at 20%).

Netdata

Key Type Default Description
url string "" The URL of a Netdata agent, for example "http://192.0.2.10:19999". Empty turns Netdata off.
ignore list of strings [] Netdata alarms to leave out, as "chart alarm" glob patterns. The match is case-sensitive.

When url is set, the bot adds the raised Netdata alarms to its own alerts. If Netdata does not answer, the alert list shows Netdata unreachable.

To leave out an alarm, add a glob for its chart and alarm name to ignore. That is the text before the value in the alert:

[netdata]
url = "http://192.0.2.10:19999"
ignore = ["/var/lib/netdata disk_space_usage"]

Jellyfin

Key Type Default Description
url string "" The URL of a Jellyfin server. Empty turns Jellyfin off.

When url is set, the System section shows the number of active streams, by direct play, direct stream, and transcode. It needs JELLYFIN_KEY in .env, or the bot does not start. Get the key from the Jellyfin dashboard, under API Keys.

Forges

A forge section shows CI results, recent runs, runners, pushes, and open issues for repositories on Forgejo or GitHub. You can add up to three [[forges]] blocks, each with up to ten repositories. The bot reads each forge every 60 seconds and only reads.

[[forges]]
type = "forgejo"
name = "Home Forge"
url = "https://forge.example.com"
token_env = "FORGEJO_TOKEN"
repos = ["owner/app", "owner/site"]
show = ["ci", "runs", "pushes", "issues"]
system_row = true
pinned = ["owner/site"]

[forges.filter]
ci = ["owner/app"]

[[forges]]
type = "github"
token_env = "GITHUB_TOKEN"
repos = ["owner/repository"]
Key Type Default Description
type string none, required "forgejo" or "github".
url string GitHub: "https://api.github.com" The server URL, http:// or https://, without /api/v1, a login, or a query. Forgejo has no default, so a Forgejo block needs it.
token_env string none, required The name of the environment variable that holds the API token. Put the token in .env. If the variable is not set, the bot does not start.
repos list of strings none, required One to ten repositories, as owner/name, each once.
show list of tables all tables of the type The tables to show, each once. See the next list.
name string "Forgejo" or "GitHub" The section title, 1 to 64 printable characters. Do not put a private host address here.
system_row boolean false true adds a short summary row to the System section. Only one forge can set it.
latest integer 5 The maximum number of items in each table, from 1 to 5.
pinned list of strings [] Repositories from repos that the tables show first. They count toward latest, so the list can have at most latest items. The Recent runs table ignores it.
[forges.filter] table {} Limits a table to some of the repositories. Each key is a table in show: ci, runs, pushes, or issues. Each value is a list of one or more repositories from repos. A table that the filter does not name uses all repositories.

The show tables are these:

  • ci: the latest CI result of each repository.
  • runs: the recent CI runs, with the result and the duration.
  • pushes: the three latest commits of each repository.
  • issues: the number of open issues and pull requests.
  • runners: Forgejo only. This version always shows runner API unavailable in this table, as a warning.

Helper

The [helper] table connects the bot to the Host helper. Omit the table to run without the helper.

Key Type Default Description
socket string none, required The absolute path of the helper socket inside the bot container, at most 107 bytes. Compose mounts the helper socket directory at /run/shazzzbot-helper.
key_env string none, required The name of the environment variable that holds the helper key. The key must be 32 or more letters, digits, _, or -. If the variable is not set or the key is not valid, the bot does not start.
[helper]
socket = "/run/shazzzbot-helper/helper.sock"
key_env = "SHAZZZBOT_HELPER_KEY"

Node update commands

/update shows the version of each node and an update command for it. A node is the bot, each relay, or the host helper. The bot only prints the command. It never runs it.

[node_updates.bot]
hostname = "server.example.com"
run_on = "workstation.example.com"
command = "cd ~/shazzzbot && ./deploy.sh server"

[node_updates.helper]
hostname = "server.example.com"
command = "bash setup.sh --upgrade"

The table name is bot, helper, or the exact RELAY_URL of a relay, in quotes. Each table accepts these keys:

Key Type Default Description
hostname string none, required The host name or IP address of the node.
run_on string the hostname value The host where you paste the command.
command string none, required One line of ASCII text, 1 to 1000 characters, with no code fences.

Preview

The [preview] table starts a web page in the bot container. The page shows the panel from the live data of the bot, and has the settings editor of the preview site. It needs a login. Omit the table, or set enabled = false, to turn the page off.

Key Type Default Description
enabled boolean false true starts the page.
bind string "0.0.0.0" The IP address in the container that the page listens on.
port integer 8780 The port that the page listens on, from 1024 to 65535.
user string "" The login name: letters, digits, _, ., or -, at most 64 characters.
password_hash string "" The hash of the login password.

Set user and password_hash together, or neither. To make the hash, run docker compose exec shazzzbot python -m shazzzbot.preview hash in the folder that holds compose.yaml, then type a password of 12 or more characters. The password does not show as you type. With neither key, the bot makes a login. It then logs the user name and the path of a file in the data volume that holds the password.

compose.yaml publishes no port for the page. To reach it, add a ports entry to the shazzzbot service, on one host address only.

The editor can save the display settings to config.toml: timezone, green_ok, order, [system], [render], [thresholds], [tasks], and [services] columns. Compose mounts config.toml read-only, so a save then fails with config.toml is read-only to the bot.

Environment variables

The bot reads secrets only from the environment, that is from .env. A secret in config.toml stops the bot with an unknown setting error.

Variable Description
DISCORD_TOKEN Required. The bot token.
JELLYFIN_KEY The Jellyfin API key. Needed when [jellyfin] url is set.
RELAY_URL, RELAY_KEY, and RELAY_FINGERPRINT The relay settings. Set all three or none. RELAY_URL must start with https://, and RELAY_FINGERPRINT must be 64 hex digits. Add _2, _3, and so on for more relays. See Relay.
SHAZZZBOT_HELPER_KEY The host helper key. The [helper] table names this variable in key_env.
the token_env of each forge The API token of that forge.
SHAZZZBOT_CONFIG The path of config.toml in the container. The default is /config/config.toml.
SHAZZZBOT_ROLE bot or relay. The default is bot.

compose.yaml also reads these variables from .env or from the shell:

Variable Default Description
SHAZZZBOT_INSTALL_ID none, required The install id that setup.sh writes. Compose labels the containers and the network with it.
HELPER_SOCKET_DIR none, required The host directory of the helper socket. The bot mounts it read-only at /run/shazzzbot-helper. Docker makes the directory when it does not exist. .env.example sets /tmp/shazzzbot-helper/socket.
HELPER_GID 10001 A group id that the bot container joins. It must agree with socket_gid in the helper configuration.
EMHTTP_DIR /var/local/emhttp The Unraid array, disk, and SMART state on the host.
SHAZZZBOT_DOCKER_SOCK /var/run/docker.sock The Docker socket that the docker-proxy container mounts.

Clone this wiki locally