
{"id":80,"date":"2025-06-06T12:53:23","date_gmt":"2025-06-06T11:53:23","guid":{"rendered":"https:\/\/blog.mkcloudai.com\/?p=80"},"modified":"2025-06-06T12:53:23","modified_gmt":"2025-06-06T11:53:23","slug":"%f0%9f%9a%80-creating-a-dynamic-motd-dashboard-for-ubuntu-ec2-with-logs-aws-metadata-alerts","status":"publish","type":"post","link":"https:\/\/blog.mkcloudai.com\/?p=80","title":{"rendered":"\ud83d\ude80 Creating a Dynamic MOTD Dashboard for Ubuntu EC2 with Logs, AWS Metadata &amp; Alerts"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\">\ud83e\udded Introduction<\/h3>\n\n\n\n<p>When you SSH into your Linux server, you\u2019re greeted with a simple Message of the Day (MOTD). But what if it could show <strong>real-time disk alerts<\/strong>, <strong>AWS metadata<\/strong>, <strong>failed services<\/strong>, and <strong>log summaries<\/strong> right away?<\/p>\n\n\n\n<p>In this blog, I\u2019ll show how I progressively customized <code>\/etc\/update-motd.d\/<\/code> to build a DevOps-style <strong>MOTD dashboard<\/strong> for my Ubuntu EC2 instance running MKCloudAI.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\ude9c Step 1: Start Simple \u2014 Basic Banner &amp; Uptime<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\uddf1 Goal<\/h3>\n\n\n\n<p>Display a welcome message and key server details.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udcc4 Script: <code>99-custom<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>#!\/bin\/bash<br><br>echo \"\"<br>echo \"\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\"<br>echo \"\u2551   \ud83d\ude80 Welcome to MKCloudAI Server     \u2551\"<br>echo \"\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\"<br>echo \"\u2551 \ud83d\udd12 Secured Ubuntu Server - DevOps    \u2551\"<br>echo \"\u2551 \ud83d\udd52 Uptime: $(uptime -p)              \u2551\"<br>echo \"\u2551 \ud83d\udca1 Tip: Keep your system updated!    \u2551\"<br>echo \"\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\"<br>echo \"\"<br>echo \"\ud83c\udf10 Hostname: $(hostname)\"<br>echo \"\ud83d\udd52 Uptime: $(uptime -p)\"<br><\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\ud83d\udd27 Place this file in <code>\/etc\/update-motd.d\/99-custom<\/code> and make it executable with <code>chmod +x<\/code>.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\ude9c Step 2: Add Disk and Memory Usage<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udcbe Disk + \ud83e\udde0 RAM<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>echo \"\ud83d\udcbe Disk Usage: $(df -h \/ | awk 'NR==2 {print $5 \" used\"}')\"<br>echo \"\ud83e\udde0 Memory Usage: $(free -m | awk '\/Mem:\/ {printf(\"%.1f%% used\\n\", $3\/$2*100)}')\"<br><\/code><\/pre>\n\n\n\n<p>Now it starts to feel useful \u2014 I can spot memory or disk issues the moment I log in.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\ude9c Step 3: Add \ud83d\udea8 Disk Usage Alert<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udea8 Goal<\/h3>\n\n\n\n<p>Trigger a warning if disk usage exceeds 80%.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>disk_usage=$(df \/ | awk 'NR==2 {gsub(\"%\",\"\",$5); print $5}')<br>if [ \"$disk_usage\" -ge 80 ]; then<br>    echo \"\ud83d\udea8 ALERT: Root disk usage is above 80% ($disk_usage%)\"<br>fi<br><\/code><\/pre>\n\n\n\n<p>Now I don\u2019t need to manually run <code>df -h<\/code> \u2014 it warns me instantly.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\ude9c Step 4: Add \u2601\ufe0f AWS EC2 Metadata<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">\u2601\ufe0f Add Instance Info Using Instance Metadata Service<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>meta_base=\"http:\/\/169.254.169.254\/latest\/meta-data\"<br>instance_id=$(curl -s ${meta_base}\/instance-id)<br>instance_type=$(curl -s ${meta_base}\/instance-type)<br>region=$(curl -s ${meta_base}\/placement\/availability-zone | sed 's\/[a-z]$\/\/')<br><br>echo \"\u2601\ufe0f AWS Instance ID: $instance_id\"<br>echo \"\u2601\ufe0f AWS Type: $instance_type | Region: $region\"<br><\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>This helps identify which EC2 instance I\u2019m on, especially when working across multiple environments.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\ude9c Step 5: Add \u274c Failed System Services<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>failed_services=$(systemctl --failed --no-legend)<br>if [ -n \"$failed_services\" ]; then<br>    echo \"\u274c Failed Services:\"<br>    echo \"$failed_services\" | awk '{print \"   \u2192 \" $1 \" (\" $2 \")\"}'<br>fi<br><\/code><\/pre>\n\n\n\n<p>This was a game-changer \u2014 no more missing broken services like <code>postfix<\/code>, <code>nginx<\/code>, or <code>docker<\/code>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\ude9c Step 6: Add \ud83d\udcc5 Cron Job Preview<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>echo \"\ud83d\udcc5 Upcoming Cron Jobs:\"<br>(crontab -l 2>\/dev\/null; sudo crontab -l 2>\/dev\/null) | grep -v '^#' | awk 'NF' | while read -r job; do<br>    echo \"   \u2192 $job\"<br>done<br><\/code><\/pre>\n\n\n\n<p>Now I can double-check scheduled jobs at a glance after each login.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\ude9c Step 7: Add \ud83d\udcdc Meaningful Log Summaries<\/h3>\n\n\n\n<p>Here\u2019s the fun part \u2014 summarizing key log files for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>syslog<\/code> (general issues)<\/li>\n\n\n\n<li><code>auth.log<\/code> (SSH logins\/failures)<\/li>\n\n\n\n<li>Apache error logs<\/li>\n\n\n\n<li>WordPress debug logs<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-preformatted\">bashCopyEdit<code>echo \"\"\necho \"\ud83d\udcdc Log Summary (last 10 relevant entries):\"\n\necho \"\u2022 Syslog:\"\ngrep -iE \"error|fail|warn\" \/var\/log\/syslog | tail -n 10 2&gt;\/dev\/null\n\necho \"\u2022 SSH Logs:\"\ngrep -iE \"Accepted|Failed|authentication failure\" \/var\/log\/auth.log | tail -n 10 2&gt;\/dev\/null\n\necho \"\u2022 Apache Errors:\"\ntail -n 10 \/var\/log\/apache2\/error.log | grep -iE \"error|warn|fail\" 2&gt;\/dev\/null\n\nwp_log=\"\/var\/www\/html\/wp-content\/debug.log\"\nif [ -f \"$wp_log\" ]; then\n    echo \"\u2022 WordPress Errors:\"\n    tail -n 10 \"$wp_log\" | grep -iE \"error|warn|notice\"\nfi\n<\/code><\/pre>\n\n\n\n<p>This gives me daily visibility of real issues \u2014 without digging through log files manually.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\ude9c Step 8: Add \u23f0 Time-Based Greetings<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>hour=$(date +\"%H\")<br>if [ \"$hour\" -lt 12 ]; then<br>    greeting=\"\ud83c\udf05 Good morning\"<br>elif [ \"$hour\" -lt 18 ]; then<br>    greeting=\"\u2600\ufe0f Good afternoon\"<br>else<br>    greeting=\"\ud83c\udf19 Good evening\"<br>fi<br><br>echo \"\u2551 $greeting!                           \u2551\"<br><\/code><\/pre>\n\n\n\n<p>A simple touch that adds personality to the login experience.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 Final Result: Beautiful, Intelligent MOTD<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">Edit<code>\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557<br>\u2551   \ud83d\ude80 Welcome to MKCloudAI Server     \u2551<br>\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563<br>\u2551 \u2600\ufe0f Good afternoon!                   \u2551<br>\u2551 \ud83d\udd12 Secured Ubuntu Server - DevOps    \u2551<br>\u2551 \ud83d\udd52 Uptime: up 7 days, 3 hours         \u2551<br>\u2551 \ud83d\udca1 Tip: Keep your system updated!    \u2551<br>\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d<br><br>\ud83c\udf10 Hostname: ip-172-26-8-36<br>\ud83d\udd52 Uptime: up 7 days, 3 hours<br>\ud83d\udcbe Disk Usage: 83% used<br>\ud83e\udde0 Memory Usage: 55.4% used<br>\ud83d\udea8 ALERT: Root disk usage is above 80% (83%)<br>\u2601\ufe0f AWS Instance ID: i-xxxxxxxxx<br>\u2601\ufe0f AWS Type: t3.micro | Region: eu-west-2<br>\u274c Failed Services:<br>   \u2192 apache2.service (failed)<br>\ud83d\udcc5 Upcoming Cron Jobs:<br>   \u2192 0 4 * * * \/usr\/bin\/apt upgrade<br>\ud83d\udcdc Log Summary:<br>\u2022 SSH Logs:<br>Failed password for root from 192.168.1.10<br>Accepted publickey for ubuntu from 172.20.1.5<br>...<br><\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udee1\ufe0f Final Tips<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Make sure your script is executable:<br><code>chmod +x \/etc\/update-motd.d\/99-custom<\/code><\/li>\n\n\n\n<li>Test it with:<br><code>run-parts \/etc\/update-motd.d\/<\/code><\/li>\n\n\n\n<li>For persistent MOTD updates, avoid static <code>\/etc\/motd<\/code>.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udccc Summary<\/h3>\n\n\n\n<p>With just one script, I turned a boring login into a <strong>DevOps dashboard<\/strong> that:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Shows alerts<\/li>\n\n\n\n<li>Tracks logs<\/li>\n\n\n\n<li>Surfaces metadata<\/li>\n\n\n\n<li>Adds personality<\/li>\n<\/ul>\n\n\n\n<p>If you&#8217;re managing AWS servers or want a smart CLI dashboard \u2014 try this out and customize it for your own workflows.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\ude4b Want the Full Script?<\/h2>\n\n\n\n<p>\ud83d\udc49 Download it from <a class=\"\" href=\"#\">GitHub Gist<\/a> (link your repo)<br>\ud83d\udc49 Or contact me via <a class=\"\" href=\"https:\/\/mkcloudai.com\">mkcloudai.com<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\ud83e\udded Introduction When you SSH into your Linux server, you\u2019re greeted with a simple Message of the Day (MOTD). But [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[5,8,4],"tags":[31,29,28,32,30,27,18],"class_list":["post-80","post","type-post","status-publish","format-standard","hentry","category-aws","category-how-to","category-linux","tag-automation","tag-aws-ec2","tag-devops","tag-logging","tag-motd","tag-system-monitoring","tag-ubuntu"],"_links":{"self":[{"href":"https:\/\/blog.mkcloudai.com\/index.php?rest_route=\/wp\/v2\/posts\/80","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.mkcloudai.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mkcloudai.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mkcloudai.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mkcloudai.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=80"}],"version-history":[{"count":1,"href":"https:\/\/blog.mkcloudai.com\/index.php?rest_route=\/wp\/v2\/posts\/80\/revisions"}],"predecessor-version":[{"id":81,"href":"https:\/\/blog.mkcloudai.com\/index.php?rest_route=\/wp\/v2\/posts\/80\/revisions\/81"}],"wp:attachment":[{"href":"https:\/\/blog.mkcloudai.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=80"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mkcloudai.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=80"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mkcloudai.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=80"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}