The Message of the Day is the first thing an administrator sees on every login β and the first thing a forensic investigator reads after an incident. A well-crafted MOTD does three jobs at once: it shows real-time system status (load, disk, recent updates), it warns unauthorized users that access is monitored, and it identifies the host so you do not run shutdown -h now on the wrong server. This guide covers static banners, dynamic motd, and the legal-warning copy your CISO will want.
The three banner files
/etc/issueβ shown at the local console login prompt (before authentication)./etc/issue.netβ shown to remote (SSH) clients before authentication. RequiresBanner /etc/issue.netin sshd_config./etc/motdβ shown after successful login, both console and SSH.
Many distributions also use the pam_motd module and a /etc/update-motd.d/ directory of scripts that compose the dynamic motd at login time.
The legal warning that lawyers want
+-----------------------------------------------------------------+
| AUTHORIZED ACCESS ONLY |
| This system is the property of Example Corp. Unauthorized access |
| is strictly prohibited. All activity is logged and monitored. |
| By continuing you consent to monitoring and acknowledge that you |
| have no expectation of privacy on this system. |
+-----------------------------------------------------------------+
Save as /etc/issue.net and add to sshd_config:
Banner /etc/issue.net
Then sudo systemctl reload ssh. The exact wording matters in some jurisdictions for prosecuting unauthorized access β confirm with counsel.
Static motd basics
For a static, server-identifying motd, write a single file:
cat <<'EOF' | sudo tee /etc/motd
================================================================
HOST : prod-db-01.example.com
ROLE : PostgreSQL primary
ENVIRONMENT: PRODUCTION
CONTACT : ops@example.com / pager 555-0100
================================================================
EOF
If you want the file to update automatically (e.g. with the actual hostname), turn it into a dynamic motd script.
Dynamic motd via update-motd.d
Each script in /etc/update-motd.d/ is executed in lexical order, and its stdout is concatenated into the final motd. Naming convention: NN-name. Example numbered scripts:
#!/bin/bash
# /etc/update-motd.d/10-hostinfo
printf 'Host : %-30s Date : %s\n' "$(hostname -f)" "$(date '+%F %T')"
printf 'Up : %-30s Users : %s\n' "$(uptime -p)" "$(who -u | wc -l)"
printf 'Load : %-30s Procs : %s\n' "$(uptime | awk -F'load average: ' '{print $2}')" "$(ps ax | wc -l)"
echo
#!/bin/bash
# /etc/update-motd.d/20-disk
printf 'Disk usage:\n'
df -h --output=target,size,used,avail,pcent -x tmpfs -x devtmpfs | awk 'NR==1 || $5+0 > 0'
echo
#!/bin/bash
# /etc/update-motd.d/30-updates
sec=$(apt list --upgradable 2>/dev/null | grep -ci security)
all=$(apt list --upgradable 2>/dev/null | tail -n +2 | wc -l)
printf 'Updates: %d available, %d security\n\n' "$all" "$sec"
Make scripts executable: sudo chmod +x /etc/update-motd.d/*. Test the rendered motd with sudo run-parts /etc/update-motd.d/.
RHEL/Fedora dynamic motd
RHEL family does not use update-motd.d. Either:
- Generate motd with a cron/timer job:
# /etc/cron.d/motd * * * * * root /usr/local/bin/build-motd > /etc/motd - Or use a PAM session script (more fragile) β usually the cron approach is simpler.
Per-user banners
If a single account has special instructions, set them in ~/.profile or via PAM in /etc/pam.d/sshd:
session optional pam_motd.so motd=/etc/motd.d
This causes pam_motd to read every file under /etc/motd.d/ and concatenate them β useful for per-team or per-environment messages.
Suppressing the default Ubuntu motd
Ubuntu ships with a chatty motd that includes ads and stats. Disable individual scripts by removing the executable bit:
sudo chmod -x /etc/update-motd.d/10-help-text
sudo chmod -x /etc/update-motd.d/50-motd-news
sudo chmod -x /etc/update-motd.d/91-contract-ua-esm-status
Or disable network-fetching motd globally by editing /etc/default/motd-news and setting ENABLED=0.
Testing the rendered output
sudo run-parts /etc/update-motd.d/ # Debian/Ubuntu
ssh user@localhost # see what real users see
cat /run/motd.dynamic # cached dynamic content
Best practices
- Always include the FQDN in the motd. The number of
shutdown -h nowon the wrong host is non-zero β make it harder. - Display the environment label (PROD vs STAGING) in colored text using ANSI escapes.
- Include a
contactline so the next on-call knows whom to ping. - Keep the motd under 25 lines β anything longer is unread.
- Validate motd scripts via shellcheck β a runtime error in motd creates noisy login output.
Common pitfalls
- Embedding live commands that can hang in motd scripts (e.g.
apt list --upgradablewith broken DNS); always set short timeouts. - Forgetting to enable the SSH Banner directive β
/etc/issue.netsits unused. - Including secrets in the motd by mistake (e.g. paste of an SSH key during testing).
- Updating motd from a cron job that runs as a non-root user lacking write permission.
The MOTD is a 30-second build that pays off every time you SSH into the wrong server, every audit that requires a banner, and every incident review that asks "did this user see the warning?" Take the time to build a useful one β your future-self at 03:00 will thank you.