{"id":293,"date":"2026-05-12T22:36:27","date_gmt":"2026-05-13T06:36:27","guid":{"rendered":"https:\/\/rainier-it.com\/blog\/?p=293"},"modified":"2026-05-13T06:53:18","modified_gmt":"2026-05-13T14:53:18","slug":"make-your-servers-smile-dynamic-motd","status":"publish","type":"post","link":"https:\/\/rainier-it.com\/blog\/make-your-servers-smile-dynamic-motd\/","title":{"rendered":"Make Your Servers Smile: Dynamic MOTDs with Figlet, Cowsay, Fortune, and Lolcat"},"content":{"rendered":"\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Every time I SSH into one of my Ubuntu boxes I&#8217;m greeted with a chunky ASCII hostname in rainbow gradient, a unicorn quoting Lewis Carroll, and a four-column block of system stats. It&#8217;s the <strong>MOTD<\/strong> \u2014 the Message of the Day \u2014 and it&#8217;s the most overlooked piece of polish on a Linux server.<\/p>\n\n\n\n<p>I think there are two camps on this. One half of the internet calls dynamic MOTDs frivolous. The other half quietly enjoys them every single time, because they accomplish four real things at once:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Confirmation you&#8217;re on the right box.<\/strong> A figlet banner of the hostname in 80-point letters is a <em>very<\/em> effective way to avoid running <code>rm -rf<\/code> on the wrong server.<\/li>\n\n\n\n<li><strong>A 1-second health check.<\/strong> Load, RAM, disk, swap, uptime, IP. If anything looks weird, you see it before you even type a command.<\/li>\n\n\n\n<li><strong>A small daily joy.<\/strong> Fortune cookies and unicorns make automation feel like yours and not Canonical&#8217;s.<\/li>\n\n\n\n<li><strong>A status-page-for-one.<\/strong> Updates pending, reboot required, ESM enrollment \u2014 Ubuntu already wires up the surface, you just have to keep it (and remove the Pro upsells).<\/li>\n<\/ol>\n\n\n\n<p>Here&#8217;s exactly what&#8217;s running on my Ansible control node <code>pegasus<\/code>, and how to put the same setup on any Ubuntu, Debian, Mint, or Pop!_OS box in about ten minutes.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"2464\" height=\"1958\" src=\"https:\/\/rainier-it.com\/blog\/wp-content\/uploads\/2026\/05\/blog-dynamic-motd-pegasus.png\" alt=\"Pegasus MOTD screenshot \u2014 figlet banner, fortune in a unicorn cowsay bubble, sysinfo block\" class=\"wp-image-292\" srcset=\"https:\/\/rainier-it.com\/blog\/wp-content\/uploads\/2026\/05\/blog-dynamic-motd-pegasus.png 2464w, https:\/\/rainier-it.com\/blog\/wp-content\/uploads\/2026\/05\/blog-dynamic-motd-pegasus-300x238.png 300w, https:\/\/rainier-it.com\/blog\/wp-content\/uploads\/2026\/05\/blog-dynamic-motd-pegasus-1024x814.png 1024w, https:\/\/rainier-it.com\/blog\/wp-content\/uploads\/2026\/05\/blog-dynamic-motd-pegasus-768x610.png 768w, https:\/\/rainier-it.com\/blog\/wp-content\/uploads\/2026\/05\/blog-dynamic-motd-pegasus-1536x1221.png 1536w, https:\/\/rainier-it.com\/blog\/wp-content\/uploads\/2026\/05\/blog-dynamic-motd-pegasus-2048x1627.png 2048w\" sizes=\"auto, (max-width: 2464px) 100vw, 2464px\" \/><figcaption class=\"wp-element-caption\">The MOTD on pegasus: figlet banner + fortune + unicorn cowsay + sysinfo block, all piped through lolcat.<\/figcaption><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udde9 How Ubuntu&#8217;s MOTD Actually Works<\/h2>\n\n\n\n<p>This is the part nobody told me when I first inherited a server. The classic <code>\/etc\/motd<\/code> is a <em>static<\/em> text file \u2014 most distros barely use it anymore. The dynamic stuff comes from a directory of small executable scripts:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\/etc\/update-motd.d\/\n\u251c\u2500\u2500 00-header                \u2190 banner line + (here be dragons)\n\u251c\u2500\u2500 10-help-text             \u2190 Ubuntu Pro\/Landscape ads (gut these)\n\u251c\u2500\u2500 10-sysinfo               \u2190 optional: system stats block\n\u251c\u2500\u2500 50-motd-news             \u2190 Canonical's news feed\n\u251c\u2500\u2500 85-fwupd                 \u2190 firmware-update notices\n\u251c\u2500\u2500 90-footer                \u2190 whatever \/etc\/motd.tail says\n\u251c\u2500\u2500 90-updates-available\n\u251c\u2500\u2500 91-contract-ua-esm-status\n\u251c\u2500\u2500 91-release-upgrade\n\u251c\u2500\u2500 92-unattended-upgrades\n\u251c\u2500\u2500 95-hwe-eol\n\u251c\u2500\u2500 98-fsck-at-reboot\n\u2514\u2500\u2500 98-reboot-required<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">\/etc\/update-motd.d\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">\u251c\u2500\u2500<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">00<\/span><span style=\"color: #A3BE8C\">-header<\/span><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #A3BE8C\">\u2190<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">banner<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">line<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">+<\/span><span style=\"color: #D8DEE9FF\"> (here <\/span><span style=\"color: #A3BE8C\">be<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">dragons<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">\u251c\u2500\u2500<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">10<\/span><span style=\"color: #A3BE8C\">-help-text<\/span><span style=\"color: #D8DEE9FF\">             <\/span><span style=\"color: #A3BE8C\">\u2190<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">Ubuntu<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">Pro\/Landscape<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">ads<\/span><span style=\"color: #D8DEE9FF\"> (gut <\/span><span style=\"color: #A3BE8C\">these<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">\u251c\u2500\u2500<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">10<\/span><span style=\"color: #A3BE8C\">-sysinfo<\/span><span style=\"color: #D8DEE9FF\">               <\/span><span style=\"color: #A3BE8C\">\u2190<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">optional:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">system<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">stats<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">block<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">\u251c\u2500\u2500<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">50<\/span><span style=\"color: #A3BE8C\">-motd-news<\/span><span style=\"color: #D8DEE9FF\">             <\/span><span style=\"color: #A3BE8C\">\u2190<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">Canonical<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #A3BE8C\">s news feed<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">\u251c\u2500\u2500 85-fwupd                 \u2190 firmware-update notices<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">\u251c\u2500\u2500 90-footer                \u2190 whatever \/etc\/motd.tail says<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">\u251c\u2500\u2500 90-updates-available<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">\u251c\u2500\u2500 91-contract-ua-esm-status<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">\u251c\u2500\u2500 91-release-upgrade<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">\u251c\u2500\u2500 92-unattended-upgrades<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">\u251c\u2500\u2500 95-hwe-eol<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">\u251c\u2500\u2500 98-fsck-at-reboot<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">\u2514\u2500\u2500 98-reboot-required<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p><code>pam_motd<\/code> runs each script in name order on login, concatenates the output, and dumps it into <code>\/run\/motd.dynamic<\/code>, which is what you actually see. Every script is just an executable file \u2014 shell, Python, Perl, whatever \u2014 that prints to stdout. If you want to change the MOTD, you don&#8217;t edit <code>\/etc\/motd<\/code>. You edit (or add) a script in <code>\/etc\/update-motd.d\/<\/code>.<\/p>\n\n\n\n<p>That&#8217;s it. That&#8217;s the whole abstraction.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udded The Recipe (Six Steps, About Ten Minutes)<\/h2>\n\n\n\n<p>Below is the entire build, top to bottom. Run each step on the server you want to reskin. Steps 1\u20134 are required for the banner-and-stats look in the screenshot above; step&nbsp;5 is an optional &#8220;remove Canonical&#8217;s ads&#8221; pass; step&nbsp;6 is how to see what you built without logging out.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1 \u2014 Install the four packages<\/h2>\n\n\n\n<p>Everything we need ships in the standard Ubuntu and Debian repositories \u2014 no PPAs, no third-party scripts, no <code>curl | bash<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>sudo apt update\nsudo apt install -y figlet cowsay fortune-mod fortunes-min lolcat<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">sudo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">apt<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">update<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">sudo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">apt<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">install<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">-y<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">figlet<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">cowsay<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">fortune-mod<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">fortunes-min<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">lolcat<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Here&#8217;s what each tool actually does:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>figlet<\/code><\/strong> \u2014 Converts ordinary text into giant ASCII letters. Use <code>figlet -f &lt;font&gt;<\/code> to pick a face; <code>figlist<\/code> shows what&#8217;s installed (and <code>apt install figlet-fonts<\/code> adds dozens more).<\/li>\n\n\n\n<li><strong><code>cowsay<\/code><\/strong> \u2014 Stuffs whatever you pipe into it into a speech bubble coming out of a cow&#8217;s mouth. The &#8220;cow&#8221; can be replaced with any <code>.cow<\/code> file \u2014 <code>cowsay -l<\/code> lists what&#8217;s available (dragons, tux, ghostbusters, stegosaurus, and yes, unicorns).<\/li>\n\n\n\n<li><strong><code>fortune-mod<\/code><\/strong> \u2014 A 30-year-old Unix curiosity that prints a random pithy quote. The <code>fortunes-min<\/code> package gives you the basic set; <code>fortunes<\/code> adds a much larger catalogue.<\/li>\n\n\n\n<li><strong><code>lolcat<\/code><\/strong> \u2014 Pipes any text through a rainbow gradient using 24-bit ANSI escape codes. Looks ridiculous on its own; looks fantastic stacking onto figlet and cowsay.<\/li>\n<\/ul>\n\n\n\n<p>Optional extras you might enjoy:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>toilet<\/code> \u2014 like figlet but with colorful filters baked in.<\/li>\n\n\n\n<li><code>boxes<\/code> \u2014 wraps text in ASCII borders (parchment, dragon, scroll, etc.).<\/li>\n\n\n\n<li><code>neofetch<\/code> \/ <code>fastfetch<\/code> \u2014 the &#8220;system info with distro logo&#8221; tool you see in screenshots of Arch users&#8217; terminals.<\/li>\n\n\n\n<li><code>sl<\/code> \u2014 when you type <code>ls<\/code> wrong, a steam locomotive crosses your screen. Not for the MOTD; just for joy.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2 \u2014 Grab a unicorn (or any character you like)<\/h2>\n\n\n\n<p>Pegasus uses a unicorn cowfile. You can use any <code>.cow<\/code> file you want here \u2014 browse <code>cowsay -l<\/code> for what came in the box, or grab one off the upstream gallery at <a href=\"https:\/\/github.com\/piuccio\/cowsay\/tree\/master\/cows\" target=\"_blank\" rel=\"noopener\">github.com\/piuccio\/cowsay\/tree\/master\/cows<\/a> (unicorns, otters, sharks, R2-D2, Pikachu, Death Star \u2026 the list keeps going).<\/p>\n\n\n\n<p>Drop the unicorn into <code>\/etc\/<\/code> so the MOTD script can find it:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>sudo curl -fsSL https:\/\/raw.githubusercontent.com\/piuccio\/cowsay\/master\/cows\/unicorn.cow \\\n    -o \/etc\/cowsay-unicorn.cow<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">sudo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">curl<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">-fsSL<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">https:\/\/raw.githubusercontent.com\/piuccio\/cowsay\/master\/cows\/unicorn.cow<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #EBCB8B\">\\<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #A3BE8C\">-o<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">\/etc\/cowsay-unicorn.cow<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Verify it before we wire it up:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>echo \"Hi\" | cowsay -f \/etc\/cowsay-unicorn.cow<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">echo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">Hi<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">|<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">cowsay<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">-f<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">\/etc\/cowsay-unicorn.cow<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>If you ever want to write your own character, <code>.cow<\/code> files are tiny Perl snippets \u2014 <code>default.cow<\/code> is the simplest example to crib from:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>$the_cow = &lt;&lt;\"EOC\";\n        $thoughts   ^__^\n         $thoughts  ($eyes)\\_______\n            (__)\\       )\\\/\\\n             $tongue ||----w |\n                ||     ||\nEOC<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9\">$the_cow<\/span><span style=\"color: #D8DEE9FF\"> = <\/span><span style=\"color: #ECEFF4\">&lt;&lt;&quot;EOC&quot;<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">        <\/span><span style=\"color: #D8DEE9\">$thoughts<\/span><span style=\"color: #A3BE8C\">   ^__^<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">         <\/span><span style=\"color: #D8DEE9\">$thoughts<\/span><span style=\"color: #A3BE8C\">  (<\/span><span style=\"color: #D8DEE9\">$eyes<\/span><span style=\"color: #A3BE8C\">)<\/span><span style=\"color: #EBCB8B\">\\_<\/span><span style=\"color: #A3BE8C\">______<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">            (__)<\/span><span style=\"color: #EBCB8B\">\\ <\/span><span style=\"color: #A3BE8C\">      )<\/span><span style=\"color: #EBCB8B\">\\\/<\/span><span style=\"color: #A3BE8C\">\\<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">             <\/span><span style=\"color: #D8DEE9\">$tongue<\/span><span style=\"color: #A3BE8C\"> ||----w |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">                ||     ||<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">EOC<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The <code>$thoughts<\/code>, <code>$eyes<\/code>, and <code>$tongue<\/code> variables let <code>cowsay -e<\/code> and <code>cowsay -T<\/code> swap the eyes and tongue at runtime.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3 \u2014 Replace the header script<\/h2>\n\n\n\n<p>This is the file that does the headline work \u2014 <code>\/etc\/update-motd.d\/00-header<\/code>. Overwrite it (or rename the original to <code>00-header.bak<\/code> first if you&#8217;re nervous):<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>sudo tee \/etc\/update-motd.d\/00-header > \/dev\/null &lt;&lt;'EOF'\n#!\/bin\/sh\n&#91; -r \/etc\/lsb-release &#93; &amp;&amp; . \/etc\/lsb-release\n\nif &#91; -z \"$DISTRIB_DESCRIPTION\" &#93; &amp;&amp; &#91; -x \/usr\/bin\/lsb_release &#93;; then\n    DISTRIB_DESCRIPTION=$(lsb_release -s -d)\nfi\n\nprintf \"Welcome to %s (%s).\\n\" \"$DISTRIB_DESCRIPTION\" \"$(uname -r)\"\n\n# Hostname in rainbow figlet\n\/usr\/bin\/env figlet \"$(hostname)\" | \/usr\/bin\/env lolcat -f\n\n# A unicorn delivering a fortune cookie\n\/usr\/bin\/env fortune -s | \/usr\/bin\/env cowsay -f \/etc\/cowsay-unicorn.cow | \/usr\/bin\/env lolcat -f\nEOF\nsudo chmod +x \/etc\/update-motd.d\/00-header<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">sudo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">tee<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">\/etc\/update-motd.d\/00-header<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">\/dev\/null<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">&lt;&lt;<\/span><span style=\"color: #D8DEE9FF\">&#39;<\/span><span style=\"color: #ECEFF4\">EOF<\/span><span style=\"color: #D8DEE9FF\">&#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">#!\/bin\/sh<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">&#91; -r \/etc\/lsb-release &#93; &amp;&amp; . \/etc\/lsb-release<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">if &#91; -z &quot;$DISTRIB_DESCRIPTION&quot; &#93; &amp;&amp; &#91; -x \/usr\/bin\/lsb_release &#93;; then<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">    DISTRIB_DESCRIPTION=$(lsb_release -s -d)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">fi<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">printf &quot;Welcome to %s (%s).\\n&quot; &quot;$DISTRIB_DESCRIPTION&quot; &quot;$(uname -r)&quot;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\"># Hostname in rainbow figlet<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">\/usr\/bin\/env figlet &quot;$(hostname)&quot; | \/usr\/bin\/env lolcat -f<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\"># A unicorn delivering a fortune cookie<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">\/usr\/bin\/env fortune -s | \/usr\/bin\/env cowsay -f \/etc\/cowsay-unicorn.cow | \/usr\/bin\/env lolcat -f<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">EOF<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">sudo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">chmod<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">+x<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">\/etc\/update-motd.d\/00-header<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Two small but important things going on in there:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <code>-f<\/code> flag on <code>lolcat<\/code> <strong>forces colour output<\/strong> even when stdout isn&#8217;t a TTY. That matters here, because <code>pam_motd<\/code> runs these scripts non-interactively. Without <code>-f<\/code>, lolcat detects &#8220;not a terminal&#8221; and prints plain text.<\/li>\n\n\n\n<li><code>fortune -s<\/code> returns only <strong>short<\/strong> fortunes. Long ones blow past the cowsay bubble and look wrong.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4 \u2014 Add the system-info block<\/h2>\n\n\n\n<p>The four-column stats block on Pegasus comes from a second script: <code>\/etc\/update-motd.d\/10-sysinfo<\/code>. It was originally written by Nick Charlton and has been battle-hardened across most Ubuntu installs since 2013.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>sudo tee \/etc\/update-motd.d\/10-sysinfo > \/dev\/null &lt;&lt;'EOF'\n#!\/bin\/bash\nprintf \"\\n\"\nload=`awk '{print $1}' \/proc\/loadavg`\nroot_usage=`df -h \/ | awk '\/\\\/\/ {print $(NF-1)}'`\nmemory_usage=`awk '\/^MemTotal:\/ {total=$2} \/^MemFree:\/ {free=$2} \/^Buffers:\/ {buffers=$2} \/^Cached:\/ {cached=$2} END { printf(\"%3.1f%%\", (total-(free+buffers+cached))\/total*100)}' \/proc\/meminfo`\nswap_usage=`awk '\/^SwapTotal:\/ { total=$2 } \/^SwapFree:\/ { free=$2} END { printf(\"%3.1f%%\", (total-free)\/total*100 )}' \/proc\/meminfo`\nusers=`users | wc -w`\ntime=`awk '{uptime=$1} END {days = int(uptime\/86400); hours = int((uptime-(days*86400))\/3600); printf(\"%d days, %d hours\", days, hours)}' \/proc\/uptime`\nprocesses=`\/bin\/ls -d \/proc\/&#91;0-9&#93;* | wc -l`\nip=`echo $(ip addr | grep -Eo 'inet (addr:)?(&#91;0-9&#93;*\\.){3}&#91;0-9&#93;*' | grep -Eo '(&#91;0-9&#93;*\\.){3}&#91;0-9&#93;*' | grep -v '127.0.0.1') | awk '{ print $1 }'`\n\nprintf \"System load:\\t%s\\t\\tIP Address:\\t%s\\n\"   \"$load\"         \"$ip\"\nprintf \"Memory usage:\\t%s\\t\\tSystem uptime:\\t%s\\n\" \"$memory_usage\" \"$time\"\nprintf \"Usage on \/:\\t%s\\t\\tSwap usage:\\t%s\\n\"    \"$root_usage\"   \"$swap_usage\"\nprintf \"Local Users:\\t%s\\t\\tProcesses:\\t%s\\n\"    \"$users\"        \"$processes\"\nEOF\nsudo chmod +x \/etc\/update-motd.d\/10-sysinfo<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">sudo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">tee<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">\/etc\/update-motd.d\/10-sysinfo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">\/dev\/null<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">&lt;&lt;<\/span><span style=\"color: #D8DEE9FF\">&#39;<\/span><span style=\"color: #ECEFF4\">EOF<\/span><span style=\"color: #D8DEE9FF\">&#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">#!\/bin\/bash<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">printf &quot;\\n&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">load=`awk &#39;{print $1}&#39; \/proc\/loadavg`<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">root_usage=`df -h \/ | awk &#39;\/\\\/\/ {print $(NF-1)}&#39;`<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">memory_usage=`awk &#39;\/^MemTotal:\/ {total=$2} \/^MemFree:\/ {free=$2} \/^Buffers:\/ {buffers=$2} \/^Cached:\/ {cached=$2} END { printf(&quot;%3.1f%%&quot;, (total-(free+buffers+cached))\/total*100)}&#39; \/proc\/meminfo`<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">swap_usage=`awk &#39;\/^SwapTotal:\/ { total=$2 } \/^SwapFree:\/ { free=$2} END { printf(&quot;%3.1f%%&quot;, (total-free)\/total*100 )}&#39; \/proc\/meminfo`<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">users=`users | wc -w`<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">time=`awk &#39;{uptime=$1} END {days = int(uptime\/86400); hours = int((uptime-(days*86400))\/3600); printf(&quot;%d days, %d hours&quot;, days, hours)}&#39; \/proc\/uptime`<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">processes=`\/bin\/ls -d \/proc\/&#91;0-9&#93;* | wc -l`<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">ip=`echo $(ip addr | grep -Eo &#39;inet (addr:)?(&#91;0-9&#93;*\\.){3}&#91;0-9&#93;*&#39; | grep -Eo &#39;(&#91;0-9&#93;*\\.){3}&#91;0-9&#93;*&#39; | grep -v &#39;127.0.0.1&#39;) | awk &#39;{ print $1 }&#39;`<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">printf &quot;System load:\\t%s\\t\\tIP Address:\\t%s\\n&quot;   &quot;$load&quot;         &quot;$ip&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">printf &quot;Memory usage:\\t%s\\t\\tSystem uptime:\\t%s\\n&quot; &quot;$memory_usage&quot; &quot;$time&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">printf &quot;Usage on \/:\\t%s\\t\\tSwap usage:\\t%s\\n&quot;    &quot;$root_usage&quot;   &quot;$swap_usage&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">printf &quot;Local Users:\\t%s\\t\\tProcesses:\\t%s\\n&quot;    &quot;$users&quot;        &quot;$processes&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">EOF<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">sudo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">chmod<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">+x<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">\/etc\/update-motd.d\/10-sysinfo<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>It reads everything from <code>\/proc\/loadavg<\/code>, <code>\/proc\/meminfo<\/code>, and <code>\/proc\/uptime<\/code> directly, so it doesn&#8217;t fork <code>top<\/code> or <code>vmstat<\/code> and adds essentially zero login latency. On a server with no swap, the swap line will read <code>-nan%<\/code> \u2014 harmless, but you can patch the awk to print <code>\"n\/a\"<\/code> if it bothers you.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5 \u2014 (Optional) Mute the Ubuntu Pro spam<\/h2>\n\n\n\n<p>By default, Ubuntu&#8217;s <code>10-help-text<\/code> script pushes three lines about Landscape, Ubuntu Pro, and documentation every time you log in. Once you&#8217;ve read them, they&#8217;re noise. Comment the body out:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>sudo sed -i 's\/^printf\/#printf\/' \/etc\/update-motd.d\/10-help-text<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">sudo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">sed<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">-i<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #A3BE8C\">s\/^printf\/#printf\/<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">\/etc\/update-motd.d\/10-help-text<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>This leaves the script in place (so the next <code>apt upgrade<\/code> won&#8217;t restore the noise) but produces no output. You&#8217;ll get a <code>.dpkg-dist<\/code> sibling file the next time the package upgrades \u2014 that&#8217;s apt being polite about your modification; you can <code>diff<\/code> it against your version when you&#8217;re curious.<\/p>\n\n\n\n<p>While you&#8217;re at it, you can disable the daily news feed entirely:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>sudo sed -i 's\/^ENABLED=1\/ENABLED=0\/' \/etc\/default\/motd-news<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">sudo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">sed<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">-i<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #A3BE8C\">s\/^ENABLED=1\/ENABLED=0\/<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">\/etc\/default\/motd-news<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 6 \u2014 See it without logging out<\/h2>\n\n\n\n<p><code>pam_motd<\/code> regenerates the cached MOTD on every interactive login, so the easiest test is just to <code>exit<\/code> and SSH back in. But there are two faster paths if you&#8217;re tuning a script and want a tight feedback loop:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Render once, right now:<\/strong> <code>sudo run-parts \/etc\/update-motd.d\/<\/code><\/li>\n\n\n\n<li><strong>Force a cache refresh:<\/strong> <code>sudo \/usr\/sbin\/update-motd<\/code> (Debian) or simply log out and back in.<\/li>\n<\/ul>\n\n\n\n<p>The <code>run-parts<\/code> command executes scripts in the exact same order <code>pam_motd<\/code> will, with the same environment, so what you see on the terminal is what your next SSH session will see.<\/p>\n\n\n\n<p>That&#8217;s the whole recipe. Six steps, one cup of coffee, one unicorn.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udde0 A Few Things I Wish I&#8217;d Known<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>The order matters.<\/strong> Files are run in <code>LC_COLLATE<\/code> order, which means <code>10-foo<\/code> runs after <code>00-bar<\/code> but before <code>90-baz<\/code>. Two-digit prefixes are the convention.<\/li>\n\n\n\n<li><strong>The scripts need to be executable.<\/strong> <code>chmod +x<\/code> or nothing renders. A surprising number of MOTD-tutorial pastes forget to mention this.<\/li>\n\n\n\n<li><strong>Don&#8217;t <code>echo<\/code> colours directly.<\/strong> Use the tools&#8217; own colour flags (<code>lolcat -f<\/code>, <code>toilet -F gay<\/code>). Raw ANSI codes mixed into your script work, but they&#8217;re a pain to maintain across distros.<\/li>\n\n\n\n<li><strong>Bash history of the previous admin is gold.<\/strong> If you inherit a server and want to know how the MOTD got built, <code>grep -E 'figlet|cowsay|update-motd' ~\/.bash_history<\/code> usually tells the whole story.<\/li>\n\n\n\n<li><strong>Cowfiles are Perl.<\/strong> They look like text art but they&#8217;re interpreted as Perl \u2014 escape your backslashes and dollar-signs carefully, or use single-quoted heredocs.<\/li>\n\n\n\n<li><strong>The figlet font catalogue is huge.<\/strong> Default is <code>standard<\/code>. Try <code>figlet -f slant<\/code>, <code>figlet -f banner3-D<\/code>, <code>figlet -f starwars<\/code>, or run <code>figlist<\/code> for the local list.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udf92 Going Further<\/h2>\n\n\n\n<p>Once the basic header is in, the next experiments are obvious:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Add a <strong>weather line<\/strong> from <code>wttr.in<\/code> \u2014 <code>curl -s 'wttr.in\/Orting?format=3'<\/code> is a one-liner that prints <code>Orting: \ud83c\udf26 +52\u00b0F<\/code> on every login.<\/li>\n\n\n\n<li>Hook in <strong>TacticalRMM agent status<\/strong> with a one-line systemd check.<\/li>\n\n\n\n<li>Show <strong>how many Docker containers<\/strong> are running with <code>docker ps -q | wc -l<\/code>.<\/li>\n\n\n\n<li>Print a <strong>today&#8217;s-tasks<\/strong> line by reading from a <code>~\/today.txt<\/code> file you keep on the box.<\/li>\n\n\n\n<li>Roll the whole thing into <strong>Ansible<\/strong>. We use a <code>motd<\/code> role that drops both scripts and the cowfile onto every host in our inventory, so the entire fleet welcomes you the same way.<\/li>\n<\/ul>\n\n\n\n<p>The MOTD is one of those tiny pieces of craft that costs nothing and pays you back every time you log in. If your servers feel like cold infrastructure, give them a face. Mine has a unicorn, and I&#8217;ve never regretted it.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><em>Christopher Engelhardt runs Rainier IT in Orting, WA. We do managed IT, SOC, and back-end infrastructure for small businesses across Pierce and King counties. If your servers stare back at you with no personality, <a href=\"https:\/\/rainier-it.com\/contact\">we can fix that<\/a>. (We can also fix the actual servers.)<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A tour of \/etc\/update-motd.d\/, the four tiny packages that build a great login banner (figlet, cowsay, fortune, lolcat), and how to drop a unicorn into your SSH greeting in under ten minutes.<\/p>\n","protected":false},"author":1,"featured_media":305,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,19,7],"tags":[11,28,14,34],"class_list":["post-293","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-linux","category-guides","category-it-management","tag-linux","tag-ansible","tag-terminal","tag-ubuntu"],"_links":{"self":[{"href":"https:\/\/rainier-it.com\/blog\/wp-json\/wp\/v2\/posts\/293","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rainier-it.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rainier-it.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rainier-it.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rainier-it.com\/blog\/wp-json\/wp\/v2\/comments?post=293"}],"version-history":[{"count":6,"href":"https:\/\/rainier-it.com\/blog\/wp-json\/wp\/v2\/posts\/293\/revisions"}],"predecessor-version":[{"id":316,"href":"https:\/\/rainier-it.com\/blog\/wp-json\/wp\/v2\/posts\/293\/revisions\/316"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rainier-it.com\/blog\/wp-json\/wp\/v2\/media\/305"}],"wp:attachment":[{"href":"https:\/\/rainier-it.com\/blog\/wp-json\/wp\/v2\/media?parent=293"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rainier-it.com\/blog\/wp-json\/wp\/v2\/categories?post=293"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rainier-it.com\/blog\/wp-json\/wp\/v2\/tags?post=293"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}