Remove the default GNU/Linux free software text and add your own custom message with system information. Tested with Raspbian GNU/Linux 13 (trixie).
MOTD = Message Of The Day, is the message that shows when you login to the raspberry pi.
See the Bash scripts repo for up to date scripts
1# Disable existing scripts
2sudo chmod -x /etc/update-motd.d/10-uname
3
4# Remove GNU free software text
5echo "" | sudo tee /etc/motd > /dev/null
1# Create new MOTD script and make it executable
2sudo touch /etc/update-motd.d/20-sysinfo
3sudo chmod +x /etc/update-motd.d/20-sysinfo
You can use any of the scripts below. You can either save them in /etc/profile.d/ or in /etc/update-motd.d/. If placing in /etc/profile.d/, the file should have a .sh extension. The contents of the script can also be placed in ~/.bashrc. Recommended place is /etc/update-motd.d/.
If you are using tput in a script in /etc/update-motd.d/, the colors will not show.
tput uses the terminfo database to output control codes (escape sequences) that tell your terminal to do things like: change text color, move the cursor, clear the screen, bold or underline text, reset formatting, etc. It’s terminal-independent — meaning it works correctly across xterm, Linux console, SSH, macOS Terminal, etc.
When PAM (Pluggable Authentication Modules) runs the MOTD scripts (those in /etc/update-motd.d/), it runs them non-interactively — there’s no real terminal (tty) attached yet. tput needs the TERM environment variable (e.g. xterm, vt100, linux) to know which escape sequences to use.
If TERM is missing or invalid, tput quietly fails to output anything — so you get no colors.
1#!/bin/bash
2# desc: Custom MOTD for Raspberry Pi
3# link: https://github.com/aamnah/bash-scripts/tree/master/misc/motd
4# file: /etc/profile.d/pi-motd.sh
5
6# Installation
7# Save the script: sudo nano /etc/profile.d/pi-motd.sh
8# Paste the script and save.
9# Make it executable: sudo chmod +x /etc/profile.d/pi-motd.sh
10# Log out and log back in — you should see the MOTD automatically
11
12let upSeconds="$(/usr/bin/cut -d. -f1 /proc/uptime)"
13let secs=$((${upSeconds}%60))
14let mins=$((${upSeconds}/60%60))
15let hours=$((${upSeconds}/3600%24))
16let days=$((${upSeconds}/86400))
17UPTIME=`printf "%d days, %02dh %02dm %02ds" "$days" "$hours" "$mins" "$secs"`
18DISK=$(df -h / | awk 'NR==2{print $3 "/" $2 " (" $5 " used)"}')
19MEMORY_USED=$(free -h | awk '/Mem/{print $3}')
20MEMORY_TOTAL=$(free -h | awk '/Mem/{print $2}')
21IP_LOCAL=$(hostname -I | awk '{print $1}')
22IP_PUBLIC=$(wget -q -O - http://icanhazip.com/ | tail)
23PROCESSES_RUNNING=$(ps ax | wc -l | tr -d " ")
24
25# get the load averages
26read one five fifteen rest < /proc/loadavg
27
28echo "$(tput setaf 2)
29 .~~. .~~. `date +"%A, %e %B %Y, %r"`
30 '. \ ' ' / .' `uname -srmo`$(tput setaf 1)
31 .~ .~~~..~.
32 : .~.'~'.~. : Uptime.............: ${UPTIME}
33 ~ ( ) ( ) ~ Memory.............: ${MEMORY_USED} (Used) / ${MEMORY_TOTAL} (Total)
34 ( : '~'.~.'~' : ) Load Averages......: ${one}, ${five}, ${fifteen} (1, 5, 15 min)
35 ~ .~ ( ) ~. ~ Running Processes..: ${PROCESSES_RUNNING}
36 ( : '~' : ) IP Addresses.......: ${IP_LOCAL} / ${IP_PUBLIC}
37 '~ .~~~. ~' Disk space.........: ${DISK}
38 '~'
39$(tput sgr0)"

1#!/bin/bash
2# desc: Custom MOTD for Raspberry Pi
3# link: https://github.com/aamnah/bash-scripts/tree/master/misc/motd
4# file: /etc/update-motd.d/20-sysinfo
5
6# Installation
7# Save the script: sudo nano /etc/update-motd.d/20-sysinfo
8# Make it executable: sudo chmod +x /etc/update-motd.d/20-sysinfo
9# Log out and log back in — you should see the MOTD automatically
10
11# Clear the Debian free software text: echo "" | sudo tee /etc/motd > /dev/null
12# Clear uname: sudo rm -rf /etc/update-motd.d/10-uname
13
14# ─── Colors for pretty output ───────────────────────────────────────────────
15GREEN="\e[32m"
16CYAN="\e[36m"
17YELLOW="\e[33m"
18RESET="\e[0m"
19
20# ─── Raspberry Pi Model ─────────────────────────────────────────────────────
21MODEL=$(tr -d '\0' < /proc/device-tree/model)
22
23# ─── CPU Usage
24# Calculate CPU usage by comparing /proc/stat over a short interval
25CPU=$(awk -v RS="" '{print $2+$4, $2+$4+$5}' /proc/stat | awk 'NR==1{u1=$1; t1=$2} NR==2{u2=$1; t2=$2} END{print (u2-u1)/(t2-t1)*100 "%"}' <(cat /proc/stat; sleep 0.5; cat /proc/stat))
26
27# ─── RAM Usage
28MEM_USED=$(free -h | awk '/Mem/{print $3}')
29MEM_TOTAL=$(free -h | awk '/Mem/{print $2}')
30
31# ─── Package Updates ────────────────────────────────────────────────────────
32# Uses apt to count available updates
33PKG_UPDATES=$(apt list --upgradable 2>/dev/null | grep -v Listing | wc -l)
34
35# ─── Network Info ───────────────────────────────────────────────────────────
36IP_LOCAL=$(hostname -I | awk '{print $1}')
37IP_PUBLIC=$(wget -q -O - http://icanhazip.com/ | tail)
38HOSTNAME=$(hostname)
39
40# ─── OS and Architecture ───────────────────────────────────────────────────
41OS=$(grep PRETTY_NAME /etc/os-release | cut -d= -f2 | tr -d '"')
42ARCH=$(uname -m)
43
44# ─── Temperature and Disk ──────────────────────────────────────────────────
45DISK=$(df -h / | awk 'NR==2{print $3 "/" $2 " (" $5 " used)"}')
46TEMP=$(vcgencmd measure_temp | cut -d= -f2)
47
48# ─── Uptime --------------──────────────────────────────────────────────────
49UPTIME=$(uptime -p | sed 's/^up //')
50
51# ─── Display MOTD ───────────────────────────────────────────────────────────
52echo -e "${CYAN}──────────────────────────────────────────────${RESET}"
53echo -e "${YELLOW}Model:${RESET} $MODEL"
54echo -e "${YELLOW}OS:${RESET} $OS"
55echo -e "${YELLOW}Architecture:${RESET} $ARCH"
56echo -e "${YELLOW}IP addresses:${RESET} $IP_LOCAL / $IP_PUBLIC"
57echo -e "${YELLOW}Hostname:${RESET} $HOSTNAME"
58echo -e "${YELLOW}Temperature:${RESET} $TEMP"
59echo -e "${YELLOW}Disk:${RESET} $DISK"
60echo -e "${YELLOW}CPU Usage:${RESET} $CPU"
61echo -e "${YELLOW}Memory:${RESET} $MEM_USED / $MEM_TOTAL"
62echo -e "${YELLOW}Uptime:${RESET} $UPTIME"
63echo ""
64echo -e "${YELLOW}$PKG_UPDATES${RESET} packages need updating."
65echo -e "${CYAN}──────────────────────────────────────────────${RESET}"

And here is one that combines the two above. This one will work in both /etc/profile.d/ and /etc/update-motd.d/ directories because a fallback for tput has been added. If placing in /etc/profile.d, save the sript with a .sh extension
1#!/bin/bash
2# desc: Custom MOTD for Raspberry Pi
3# link: https://github.com/aamnah/bash-scripts/tree/master/misc/motd
4# file: /etc/update-motd.d/30-sysinfo-with-logo
5
6# Installation
7# Save the script: sudo nano /etc/update-motd.d/30-sysinfo-with-logo
8# Make it executable: sudo chmod +x /etc/update-motd.d/30-sysinfo-with-logo
9# Log out and log back in — you should see the MOTD automatically
10
11# Clear the Debian free software text: echo "" | sudo tee /etc/motd > /dev/null
12# Clear uname: sudo rm -rf /etc/update-motd.d/10-uname
13
14# COLOR SETUP
15# ------------------------------------------------------------
16# Fallback for TERM if undefined
17export TERM=${TERM:-xterm}
18
19# Safe color setup
20if [ -t 1 ]; then
21 GREEN=$(tput setaf 2)
22 RED=$(tput setaf 1)
23 YELLOW=$(tput setaf 3)
24 CYAN=$(tput setaf 6)
25 RESET_COLOR=$(tput sgr0)
26else
27 GREEN="\e[32m"
28 RED="\e[31m"
29 YELLOW="\e[33m"
30 CYAN="\e[36m"
31 RESET_COLOR="\033[0m"
32fi
33
34# GATHER SYSTEM INFORMATION
35# ------------------------------------------------------------
36MODEL=$(tr -d '\0' < /proc/device-tree/model)
37
38# Calculate CPU usage by comparing /proc/stat over a short interval
39CPU=$(awk -v RS="" '{print $2+$4, $2+$4+$5}' /proc/stat | awk 'NR==1{u1=$1; t1=$2} NR==2{u2=$1; t2=$2} END{print (u2-u1)/(t2-t1)*100 "%"}' <(cat /proc/stat; sleep 0.5; cat /proc/stat))
40PROCESSES_RUNNING=$(ps ax | wc -l | tr -d " ")
41
42MEMORY_USED=$(free -h | awk '/Mem/{print $3}')
43MEMORY_TOTAL=$(free -h | awk '/Mem/{print $2}')
44
45# Uses apt to count available updates
46PKG_UPDATES=$(apt list --upgradable 2>/dev/null | grep -v Listing | wc -l)
47
48IP_LOCAL=$(hostname -I | awk '{print $1}')
49IP_PUBLIC=$(wget -q -O - http://icanhazip.com/ | tail)
50HOSTNAME=$(hostname)
51
52OS=$(grep PRETTY_NAME /etc/os-release | cut -d= -f2 | tr -d '"')
53ARCH=$(uname -m)
54
55DISK=$(df -h / | awk 'NR==2{print $3 "/" $2 " (" $5 " used)"}')
56TEMP=$(vcgencmd measure_temp | cut -d= -f2)
57
58# UPTIME=$(uptime -p | sed 's/^up //')
59let upSeconds="$(/usr/bin/cut -d. -f1 /proc/uptime)"
60let secs=$((${upSeconds}%60))
61let mins=$((${upSeconds}/60%60))
62let hours=$((${upSeconds}/3600%24))
63let days=$((${upSeconds}/86400))
64UPTIME=`printf "%d days, %02dh %02dm %02ds" "$days" "$hours" "$mins" "$secs"`
65
66
67echo -e "${GREEN}
68 .~~. .~~. ${MODEL}
69 '. \ ' ' / .' ${OS} - ${ARCH} ${RESET_COLOR}${RED}
70 .~ .~~~..~.
71 : .~.'~'.~. : IP Addresses.......: ${IP_LOCAL} / ${IP_PUBLIC}
72 ~ ( ) ( ) ~ Memory.............: ${MEMORY_USED} (Used) / ${MEMORY_TOTAL} (Total)
73 ( : '~'.~.'~' : ) Temperature........: ${TEMP}
74 ~ .~ ( ) ~. ~ CPU Usage..........: ${CPU} - ${PROCESSES_RUNNING} processes running
75 ( : '~' : ) Uptime.............: ${UPTIME}
76 '~ .~~~. ~' Disk space.........: ${DISK}
77 '~'
78${RESET_COLOR}"
79
80echo "${PKG_UPDATES} package updates available"

The GNU/Linux text is coming from the /etc/motd file. You can clear the Debian free software text by emptying the file
1echo "" | sudo tee /etc/motd > /dev/null
uname text is:
Linux raspi 6.12.47+rpt-rpi-2712 #1 SMP PREEMPT Debian 1:6.12.47-1+rpt1 (2025-09-16) aarch64
which is not needed because the uname command can be run any time if we need to see that info
either delete the /etc/update-motd.d/10-uname file
1sudo rm -rf /etc/update-motd.d/10-uname
or make it not executable
1sudo chmod -x /etc/update-motd.d/10-uname
The last login is coming from the SSH daemon. You can remove last login text if you want by changing PrintLastLog yes in /etc/ssh/sshd_config to PrintLastLog no
PrintLastLog no