motd
A message-of-the-day generator that's half facts, half feelings.
Like fastfetch, but it tells you that your VPN is down, your scrub is overdue, and you have 12 packages to upgrade — instead of telling you which kernel you're running for the seven-thousandth time. With a friendly animal.
▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆ host cube
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ up 4d 12h 31m
▒▒██████████▒▒▒▒ load 0.42 0.38 0.31
▒▒██████████▒▒▒▒ disk 97G / 888G (10%)
▒▒██████████▒▒▒▒ 204G / 32T (0%)
▒▒▒██▒▒▒▒██▒▒▒▒▒ mem 8G / 16G (50%)
▒▒▒██▒▒▒▒██▒▒▒▒▒ ip 10.10.10.10 (eth)
100.121.19.125 (tailscale)
● 2 services running → motd services
⚠ 12 packages outdated → motd updates
(That's an elephant. There are 36 other animals to choose from.)
What it does
- Mark on the left: a colorful animal of your choosing — pick a different one per host so you know where you logged in
- Facts on the right: hostname, uptime, load, disk, memory, IP — all driven by tiny built-in collectors so it's <50ms warm
- Alerts below: each module reports a one-liner only when something's worth saying; the line vanishes when it's not
- Drill-down:
motd <name> runs that module's full version (e.g. motd disk shows a per-mount breakdown with bars + ZFS datasets)
--watch mode: edit the config in one pane, watch motd re-render on save in another
- Cache where it makes sense: per-module TTL for things that genuinely don't change often, no cache for things that respond to user action
Install
From a release
curl -L https://github.com/arne/motd/releases/latest/download/motd_Linux_x86_64.tar.gz | tar xz
sudo install -m 0755 motd /usr/local/bin/motd
sudo mkdir -p /etc/xdg/motd
sudo cp -r examples/marks /etc/xdg/motd/
sudo cp examples/system-config.yaml /etc/xdg/motd/config.yaml
From source
go install github.com/arne/motd/cmd/motd@latest
git clone https://github.com/arne/motd
sudo mkdir -p /etc/xdg/motd
sudo cp -r motd/examples/marks /etc/xdg/motd/
sudo cp motd/examples/system-config.yaml /etc/xdg/motd/config.yaml
Now motd works for any user on the box. Each user can override with their own ~/.config/motd/config.yaml.
Hook it into your shell
# fish
echo "motd" >> ~/.config/fish/config.fish
# bash
echo "motd" >> ~/.bashrc
# zsh
echo "motd" >> ~/.zshrc
Configuration
Single YAML file. Two halves: layout (a recursive tree of stacks) and modules (named things that produce text).
mark: { file: marks/animals/octopus.ansi }
layout:
vstack:
- hstack:
- mark
- vstack: [host, up, load, disk, mem, ip]
- vstack: [services, updates, scrub]
modules:
host: { builtin: hostname }
up: { builtin: uptime }
load: { builtin: load }
mem: { builtin: mem }
ip: { builtin: ip }
disk: { builtin: disk }
services:
builtin: services
list:
- { name: immich, http: "http://localhost:2283/api/server/ping" }
- { name: openwebui, cmd: "incus exec openwebui -- curl -sf http://localhost:8080/health" }
updates:
command: |
n=$(apt list --upgradable 2>/dev/null | tail -n +2 | wc -l)
[ "$n" -gt 0 ] && echo "$n packages outdated"
command_full: "apt list --upgradable"
scrub:
command: |
last=$(zpool status storage 2>/dev/null | grep -oP 'on \K.*$' | head -1)
if [ -n "$last" ]; then
days=$(( ( $(date +%s) - $(date -d "$last" +%s) ) / 86400 ))
if [ "$days" -gt 30 ]; then
echo "storage scrub last ran ${days}d ago"
fi
fi
command_full: "zpool status -v"
cache: 6h
Modules
Built-ins
| Name |
What it shows |
hostname |
The hostname, in your terminal's primary accent |
uptime |
4d 12h 31m, friendly format |
load |
0.42 0.38 0.31 |
mem |
8G / 16G (50%) from /proc/meminfo |
ip |
Non-loopback IPv4 addresses, with friendly category labels (eth, wifi, tailscale, incus, docker, wg, vpn, zerotier, ...) |
disk |
Root + auto-detected zpool roots; motd disk shows per-mount table with utilization bars and ZFS datasets |
services |
Heterogeneous health checks: http, tcp, unit (systemd), or cmd. motd services shows a per-service status table |
Custom
Anything you can put in a shell command. Output goes through the styling pipeline.
weather:
command: "curl -s wttr.in/?format=3"
command_full: "curl -s wttr.in/"
cache: 30m
uptime_kuma:
command: "~/bin/check-status-page"
Convention — for command-based modules:
- Empty stdout = silent (no alert renders)
- Any stdout = treated as a warning, framework prepends ⚠ glyph and appends drill-down hint
- Exit nonzero = treat as failure; keep last good cache, don't overwrite
command_full is what motd <name> runs — use it to dump the verbose view
Marks
37 cute animals live in examples/marks/animals/. Pick one per host to differentiate where you logged in:
bat bear bee butterfly cat cow crab dog
dolphin dragon duck elephant fox frog giraffe
hamster hedgehog koala lion llama monkey mouse
octopus otter owl panda penguin pig rabbit
raccoon shark sloth tiger turtle unicorn whale wolf
Want one we don't ship? Find its emoji codepoint at openmoji.org and run:
scripts/make-animal-mark.sh <name> <CODEPOINT>
# e.g. scripts/make-animal-mark.sh chicken 1F414
The pipeline: download OpenMoji SVG → render to high-res PNG → trim → chafa-render to colored ASCII at 24×14 cells.
Drill-down
Each module is also accessible directly:
motd services # full status table for all services
motd disk # per-mount breakdown with bars + ZFS datasets
motd updates # apt list --upgradable
If a module has a command_full set, that's what runs. Builtins like disk and services ship their own drill-down.
--watch
motd --watch
Re-renders whenever the config or any referenced file changes. Edit YAML in one pane, see the result in another. Ctrl+C to exit. Watches the config file's directory and the mark file's directory; debounces multi-event editor saves.
Why not fastfetch / cowsay?
|
fastfetch |
cowsay |
motd |
| System facts |
✓ |
|
✓ |
| Actionable alerts |
|
|
✓ |
| Configurable layout |
partial |
|
✓ |
| Drill-down |
|
|
✓ |
| Friendly animal |
|
✓ |
✓ |
| Re-renders on config save |
|
|
✓ |
| Cute, in 50ms |
|
|
✓ |
Configuration file lookup
In order:
--config <path> flag
$MOTD_CONFIG env var
~/.config/motd/config.yaml (if it exists)
/etc/xdg/motd/config.yaml (system fallback)
Mark file paths in config are resolved relative to the config file's directory, so marks/animals/elephant.ansi works whether you're using the system or user config.
Building from source
git clone https://github.com/arne/motd
cd motd
go build -o motd ./cmd/motd
./motd --config examples/config.yaml
Releases are built via goreleaser on tag push. See .goreleaser.yml for the build matrix.
License
MIT. See LICENSE.