I wrote a simple web based monitoring tool with a CGI Script and used HTMX for the statistics to refresh automatically.

When looking for an admin interface to use, I noticed that every single one I was able to find, had way too much stuff going on for my use case (too many features i would never use), or they are not FOSS, which I didn't want for my server. This is when I decided that it would make sense to build my own!

So I started researching on how I could get some system statistics and a shell on a website. Then I discovered CGI scripts (Common Gateway Interface): A way to write shell commands inside HTML tags and then let it display with a webserver. This is quite an old technique, introduced, as of my research in the early 1990s, but still enough for my usecase.

The information I found on the use of CGI scripts was mainly focused on the Apache2 webserver, so I had to find a way to use them with Nginx. This took some more research and tinkering but I eventually got it working after some difficulties.

To get it working, I used fastcgi (in debian the package is called "fcgiwrap ") and some changes in my Nginx configuration (/etc/nginx/sites-available/default):

server {
  listen PORT;
  server_name NAME;

  root /var/www/html;
  index index.html;

  location /cgi-bin/ {
      alias /usr/lib/cgi-bin/;                # Default path for CGI scripts
      fastcgi_pass unix:/var/run/fcgiwrap.socket;
      include fastcgi_params;
      fastcgi_param SCRIPT_FILENAME /usr/lib$fastcgi_script_name;
  }
}

Particularly I had to add the lines in the "location" block.

Then I had to write the CGI script itself. This is what I wrote:

#!/bin/sh

echo "Content-Type: text/html"
echo ""
echo ""
echo ""
echo ""

echo "  "
echo "  BOP Admin Panel"
echo "  "
echo "  "
echo "    body { font-family: Arial, sans-serif; color: #ffffff; background-color: #000000; }"
echo "    .section { margin-bottom: 20px; }"
echo "    .section h2 { margin: 0; }"
echo "    pre { color: #ffffff; font-size: 14px; white-space: pre-wrap; }"
echo "  "
echo ""
echo ""

# CPU Temperature
echo ""
echo "CPU Temperature:"
if command -v sensors >/dev/null 2>&1; then
    echo "$(sensors | grep Core | awk '{print $1, $2, $3}')"
else
    echo "Command 'sensors' not available."
fi
echo ""

# CPU Usage
echo ""
echo "CPU Usage:"
echo ""
(
    # First measurement
    awk '/^cpu[0-9]+/ {print $1, $2+$3+$4+$5+$6, $5}' /proc/stat > /tmp/stat1
    sleep 1
    # Second measurement
    awk '/^cpu[0-9]+/ {print $1, $2+$3+$4+$5+$6, $5}' /proc/stat > /tmp/stat2

    # Calculation & Output
    awk '
        NR==FNR {
            idle_before[$1]=$3
            total_before[$1]=$2
            next
        }

        /^cpu[0-9]+/ {
            idle[$1]=$3
            total[$1]=$2
            if (length(idle_before[$1])) {
                idle_diff = idle[$1] - idle_before[$1]
                total_diff = total[$1] - total_before[$1]
                usage = (total_diff > 0) ? (100 * (1 - (idle_diff / total_diff))) : 0
                core = substr($1, 4)
                printf "Core %d: %.1f%%\n", core, usage
            }
        }' /tmp/stat1 /tmp/stat2
)
echo ""
echo ""

# RAM Usage
echo ""
echo "RAM Usage:"
echo ""
echo "$(awk '/MemTotal/ {total=$2} /MemAvailable/ {available=$2} END {usage=((total-available)/total)*100; printf "Percent: %.2f%%\n", usage}' /proc/meminfo)"
echo "$(awk '/MemTotal/ {total=$2} /MemAvailable/ {available=$2} END {printf "MiB: %d / %d MiB\n", (total-available)/1024, total/1024}' /proc/meminfo)"
echo ""
echo ""

# Uptime
echo ""
echo "Uptime:"
echo "$(uptime -p | sed 's/up //')"
echo ""
echo ""

# Removing temporary files
rm /tmp/stat1 /tmp/stat2

It displays the CPU Temperature, CPU Usage, RAM Usage in MiB and percentage and the uptime.

I them edited my index.html: I inserted HTMX into it, so that the statistics get refreshed once a second.

This creates a div and swaps its content to the content of the CGI-script every one second via HTMX.

Info on CSS, title, etc: I figured out, to change the title, favicon and CSS of your index.html, you might have to change it in the CGI-script instead of your HTML style tag or external stylesheet if you plan on doing something similar.

To have shell access in my small "admin panel", I installed ShellInABox and created an iframe that contains it into my HTML. This is what it looks like in the end:


23rd August 2024 | 09:15 AM

I did not really like the look of the admin panel, so I changed it: I let the statistics display next to each other and put them into boxes with borders, so that they can be differentiated from each other much easier.


I also added the disk usage to the statistics. I also planned on adding disk I/O and netin / netout statistics, but I did not figure out how to grab that info in a proper way yet.