Skip to content

Instantly share code, notes, and snippets.

@lbussy
Last active March 8, 2026 19:47
Show Gist options
  • Select an option

  • Save lbussy/3be88f4d10de41f110adf9e2cf54c9f2 to your computer and use it in GitHub Desktop.

Select an option

Save lbussy/3be88f4d10de41f110adf9e2cf54c9f2 to your computer and use it in GitHub Desktop.
Change the MOTD to include helpful informaiton, expecially on a Raspberry Pi.

MOTD Manager Documentation

Overview

motd-manager.sh is a Bash utility for managing a clean, colored dynamic MOTD (Message of the Day) on Raspberry Pi or Debian-based systems. It replaces the default kernel banner and legal boilerplate with a concise, informative, and visually distinct MOTD that includes host information, OS version, uptime, network details, and board type (if running on a Raspberry Pi).


Quick Install

curl -fsSL https://gist.githubusercontent.com/lbussy/3be88f4d10de41f110adf9e2cf54c9f2/raw/motd-manager.sh | sudo bash -s install

Features

  • Displays system summary with colorized output:

    • Hostname
    • Fully qualified domain name (or {hostname}.local)
    • IP address
    • OS release name and version
    • CPU architecture
    • Uptime (pretty format)
    • Primary network interface and IP address
    • Detected Raspberry Pi board model (if applicable)
  • Creates a persistent /etc/update-motd.d/90-release script.

  • Disables default MOTD snippets (kernel banner and Debian legal text).

  • Supports restoring the default MOTD configuration.

  • Compatible with both SSH and local console logins.


Installation

1. Copy the script

Save the file to /usr/local/bin/motd-manager.sh:

sudo tee /usr/local/bin/motd-manager.sh >/dev/null <<'EOF'
# (paste the full script contents here)
EOF

Make it executable:

sudo chmod +x /usr/local/bin/motd-manager.sh

2. Install the dynamic MOTD

Run the installer to create the colorized MOTD and disable the defaults:

sudo motd-manager.sh install

This will:

  • Create /etc/update-motd.d/90-release.
  • Disable /etc/update-motd.d/10-uname (kernel banner).
  • Rename /etc/motd to /etc/motd.static (hiding the legal text).

Usage

Display system info immediately

motd-manager.sh show

Example output:

\033[1;33mHost:\033[0m    wspr4
\033[1;33mSystem:\033[0m  Debian GNU/Linux 12 (bookworm)  (\033[1;36maarch64\033[0m)
\033[1;33mBoard:\033[0m   Raspberry Pi 4 Model B Rev 1.5
\033[1;32mUptime:\033[0m  5 days, 17 hours, 52 minutes
\033[1;36mAddress:\033[0m 192.168.1.64 on wlan1

Install dynamic MOTD

sudo motd-manager.sh install

This sets up the clean MOTD and removes redundant information.

Expected terminal output:

[sudo] password for pi:
Installing dynamic MOTD.
✅ Created /etc/update-motd.d/90-release
✅ Disabled /etc/update-motd.d/10-uname
✅ Moved /etc/motd → /etc/motd.static
Dynamic MOTD installed successfully.

Restore defaults

sudo motd-manager.sh restore

This restores the original Debian MOTD behavior:

  • Re-enables /etc/update-motd.d/10-uname.
  • Restores /etc/motd from /etc/motd.static if available.

Example output:

Restoring MOTD defaults.
✅ Restored /etc/update-motd.d/10-uname
✅ Restored /etc/motd from /etc/motd.static
MOTD defaults restored successfully.

Implementation Details

Raspberry Pi detection

If the system exposes /proc/device-tree/model, its contents are printed as the board model line. This includes variants like:

  • Raspberry Pi 4 Model B Rev 1.5
  • Raspberry Pi Zero 2 W Rev 1.0

Color handling

Color is always enabled for consistent SSH and console display. ANSI escape sequences are used:

  • Yellow (\033[1;33m) – host and system name
  • Cyan (\033[1;36m) – architecture and IP address
  • Green (\033[1;32m) – uptime

Safe operation

  • All file changes require root privileges (via sudo).
  • If sudo is unavailable and the script isn’t run as root, it exits gracefully with an error.
  • The set -euo pipefail safety mode prevents partial changes on errors.

Files Affected

File Purpose
/etc/update-motd.d/90-release Custom MOTD snippet with system summary
/etc/update-motd.d/10-uname Disabled (kernel version banner)
/etc/motd/etc/motd.static Legal text backup

License

This script is released under the MIT License. You are free to use, modify, and distribute it with attribution.


Version History

Version Date Notes
1.0 Nov 2025 Initial release with Pi board detection and colored MOTD support
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# @file motd-manager.sh
# @brief Manage a clean, colored dynamic MOTD for Raspberry Pi or Debian hosts.
# @details This script can preview the MOTD content, install a dynamic MOTD
# block into /etc/update-motd.d, or restore the default MOTD behavior.
# -----------------------------------------------------------------------------
set -euo pipefail
# -----------------------------------------------------------------------------
# @brief Print a message to stderr.
# @param $1 Message text.
# -----------------------------------------------------------------------------
log_error() {
local message="${1:-Unknown error}"
printf "%s\n" "$message" >&2
}
# -----------------------------------------------------------------------------
# @brief Resolve the host FQDN for display.
# @details This prefers the system FQDN. If only the short hostname is
# available, it attempts to append the configured domain. If no domain
# is configured, it falls back to "hostname.local".
# @return Prints the resolved FQDN to stdout.
# -----------------------------------------------------------------------------
get_fqdn() {
local host fqdn domain
host="$(hostname)"
fqdn="$(hostname -f 2>/dev/null || true)"
if [[ -z "$fqdn" || "$fqdn" == "$host" ]]; then
domain="$(hostname -d 2>/dev/null || true)"
if [[ -n "$domain" ]]; then
fqdn="${host}.${domain}"
else
fqdn="${host}.local"
fi
fi
printf "%s\n" "$fqdn"
}
# -----------------------------------------------------------------------------
# @brief Print host, FQDN, IP, OS release, architecture, bitness, uptime,
# and Pi board.
# @param $1 Optional debug flag.
# -----------------------------------------------------------------------------
show_system_info() {
local debug="${1:-}"
# Colors.
local Y="\033[1;33m"
local C="\033[1;36m"
local G="\033[1;32m"
local R="\033[0m"
if [[ -n "$debug" ]] && [[ $(type -t debug_print 2>/dev/null || true) == "function" ]]; then
debug_print "show_system_info called" "$debug"
fi
local host fqdn rel arch bitness uptime_str
local primary_if primary_ip pi_model
host="$(hostname)"
fqdn="$(get_fqdn)"
if [[ -r /etc/os-release ]]; then
# shellcheck disable=SC1091
. /etc/os-release
rel="${PRETTY_NAME:-${NAME}}"
else
rel="$(lsb_release -ds 2>/dev/null || printf "%s" "Unknown")"
fi
arch="$(uname -m)"
bitness="$(getconf LONG_BIT 2>/dev/null || printf "%s" "?")"
uptime_str="$(uptime -p | sed 's/^up //')"
primary_if="$(ip -o -4 route show default 2>/dev/null | awk '{print $5; exit}')"
if [[ -n "$primary_if" ]]; then
primary_ip="$(ip -o -4 addr show "$primary_if" 2>/dev/null | \
awk '{print $4; exit}' | cut -d/ -f1)"
else
primary_ip=""
fi
if [[ -z "$primary_ip" ]]; then
primary_ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
fi
if [[ -r /proc/device-tree/model ]]; then
pi_model="$(tr -d '\0' </proc/device-tree/model 2>/dev/null)"
else
pi_model=""
fi
printf "\nHost: ${Y}%s${R}\n" "$host"
printf "FQDN: ${C}%s${R}\n" "$fqdn"
if [[ -n "$primary_if" && -n "$primary_ip" ]]; then
printf "Address: ${C}%s${R} on ${Y}%s${R}\n" "$primary_ip" "$primary_if"
elif [[ -n "$primary_ip" ]]; then
printf "Address: ${C}%s${R}\n" "$primary_ip"
else
printf "Address: ${C}%s${R}\n" "Unavailable"
fi
printf "System: ${Y}%s${R} (${C}%s, %s-bit${R})\n" "$rel" "$arch" "$bitness"
if [[ -n "$pi_model" ]]; then
printf "Board: ${Y}%s${R}\n" "$pi_model"
fi
printf "Uptime: ${G}%s${R}\n" "$uptime_str"
}
# -----------------------------------------------------------------------------
# @brief Install colored dynamic MOTD and hide the default kernel banner.
# @param $1 Optional debug flag.
# -----------------------------------------------------------------------------
motd_install_release_block() {
local debug="${1:-}"
local SUDO script_path
SUDO=""
if [[ $EUID -ne 0 ]]; then
SUDO="sudo"
fi
if [[ -n "$debug" ]] && [[ $(type -t debug_print 2>/dev/null || true) == "function" ]]; then
debug_print "Installing dynamic MOTD" "$debug"
fi
script_path="/etc/update-motd.d/90-release"
$SUDO tee "$script_path" >/dev/null <<'MOTD_EOF'
#!/bin/bash
# MOTD: host, FQDN, IP, OS release, architecture, bitness, uptime, and Pi board.
set -u
get_fqdn() {
local host fqdn domain
host="$(hostname)"
fqdn="$(hostname -f 2>/dev/null || true)"
if [ -z "$fqdn" ] || [ "$fqdn" = "$host" ]; then
domain="$(hostname -d 2>/dev/null || true)"
if [ -n "$domain" ]; then
fqdn="${host}.${domain}"
else
fqdn="${host}.local"
fi
fi
printf "%s\n" "$fqdn"
}
Y="\033[1;33m"
C="\033[1;36m"
G="\033[1;32m"
R="\033[0m"
host="$(hostname)"
fqdn="$(get_fqdn)"
if [ -r /etc/os-release ]; then
. /etc/os-release
rel="${PRETTY_NAME:-${NAME}}"
else
rel="$(lsb_release -ds 2>/dev/null || echo "Unknown")"
fi
arch="$(uname -m)"
bitness="$(getconf LONG_BIT 2>/dev/null || echo "?")"
uptime_str="$(uptime -p | sed 's/^up //')"
primary_if="$(ip -o -4 route show default 2>/dev/null | awk '{print $5; exit}')"
if [ -n "$primary_if" ]; then
primary_ip="$(ip -o -4 addr show "$primary_if" 2>/dev/null | awk '{print $4; exit}' | cut -d/ -f1)"
else
primary_ip=""
fi
if [ -z "$primary_ip" ]; then
primary_ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
fi
if [ -r /proc/device-tree/model ]; then
pi_model="$(tr -d '\0' </proc/device-tree/model 2>/dev/null)"
else
pi_model=""
fi
printf "\nHost: ${Y}%s${R}\n" "$host"
printf "FQDN: ${C}%s${R}\n" "$fqdn"
if [ -n "$primary_if" ] && [ -n "$primary_ip" ]; then
printf "Address: ${C}%s${R} on ${Y}%s${R}\n" "$primary_ip" "$primary_if"
elif [ -n "$primary_ip" ]; then
printf "Address: ${C}%s${R}\n" "$primary_ip"
else
printf "Address: ${C}%s${R}\n" "Unavailable"
fi
printf "System: ${Y}%s${R} (${C}%s, %s-bit${R})\n" "$rel" "$arch" "$bitness"
if [ -n "$pi_model" ]; then
printf "Board: ${Y}%s${R}\n" "$pi_model"
fi
printf "Uptime: ${G}%s${R}\n" "$uptime_str"
MOTD_EOF
$SUDO chmod 0755 "$script_path"
if [[ -x /etc/update-motd.d/10-uname ]]; then
$SUDO chmod -x /etc/update-motd.d/10-uname
fi
if [[ -e /etc/motd && ! -e /etc/motd.static ]]; then
$SUDO mv /etc/motd /etc/motd.static
fi
}
# -----------------------------------------------------------------------------
# @brief Restore Debian defaults for MOTD display.
# @param $1 Optional debug flag.
# -----------------------------------------------------------------------------
motd_restore_defaults() {
local debug="${1:-}"
local SUDO script_path
SUDO=""
if [[ $EUID -ne 0 ]]; then
SUDO="sudo"
fi
if [[ -n "$debug" ]] && [[ $(type -t debug_print 2>/dev/null || true) == "function" ]]; then
debug_print "Restoring default MOTD behavior" "$debug"
fi
script_path="/etc/update-motd.d/90-release"
if [[ -e "$script_path" ]]; then
$SUDO rm -f "$script_path"
fi
if [[ -e /etc/update-motd.d/10-uname ]]; then
$SUDO chmod +x /etc/update-motd.d/10-uname
fi
if [[ -e /etc/motd.static && ! -e /etc/motd ]]; then
$SUDO mv /etc/motd.static /etc/motd
fi
}
# -----------------------------------------------------------------------------
# @brief Print usage information.
# @param $1 Script name.
# -----------------------------------------------------------------------------
show_usage() {
local script_name="${1:-${0##*/}}"
printf "Usage: %s {show|install|restore}\n" "$script_name" >&2
}
# -----------------------------------------------------------------------------
# @brief Main dispatcher.
# @param $1 Command.
# -----------------------------------------------------------------------------
main() {
local command="${1:-show}"
case "$command" in
show)
show_system_info
;;
install)
motd_install_release_block
;;
restore)
motd_restore_defaults
;;
*)
show_usage "${0##*/}"
exit 1
;;
esac
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment