Building a Python memory monitor for real-time system insights
Working on servers out here in the sunburnt country often means dealing with kit that has to handle everything from a sweltering Perth data centre to a chilly Hobart office. When a workstation starts chucking a wobbly because the browser has eaten half the RAM again, knowing exactly how much memory is left can save you from a long arvo of guesswork. A small Python script that watches memory in real time is one of those bits of tooling that pays for itself the moment something starts running hot.
Phil Schwartz has spent years building open-source utilities like DenyHosts and Kodos, so the approach here leans on the same minimal, no-frills Python that runs anywhere. The goal is a script that sits in a terminal and quietly updates a memory readout, the sort of thing you might leave running on a dev box in Melbourne while you grab a flat white.
By the end of this walkthrough you will have a working memory watcher using the psutil library, with output that an ops team in Sydney or a hobbyist in Adelaide can both understand. No fancy GUI, no heavy framework, just a couple of hundred lines of clean Python that you can drop into a cron job or a systemd service.
Getting the environment ready
Before touching any code, the first thing is to sort out the Python environment. Most modern Aussie dev machines already ship with Python 3.10 or later, whether you are running a MacBook in Brisbane or a Linux box on the NBN in Fitzroy. A quick python3 --version at the terminal will tell you where you stand.
The only external dependency is psutil, a cross-platform library that hands back system metrics without forcing you to parse /proc/meminfo by hand. The usual pip install does the job, and using a virtual environment keeps psutil scoped to this little tool. On a workstation inside a corporate network at a bank in the CBD, pinning the version in a requirements file will save the sysadmins from chasing phantom bugs later.
One small thing worth noting if you are on a recent Linux distro powering AWS instances in the Sydney region: psutil picks up cgroup limits automatically, which is handy when you are running inside a container. That means the numbers you see reflect what your process can actually use, not the raw host totals.
Pulling memory stats with psutil
Once psutil is installed, the heavy lifting is handled by psutil.virtual_memory(). This call returns a named tuple with total, available, percent, used, free, and a few other fields covering swap and cached pages. For a real-time monitor, the most useful values are available and percent, since available is what the kernel is actually willing to hand out to new processes right now.
A neat trick is to also grab psutil.swap_memory() to see when the system has started spilling into swap. On a busy build server in Canberra, watching swap climb is often the first sign that a runaway compiler is chewing through resources, and you can react before things grind to a halt. The values come back in bytes, so dividing by 1024 ** 3 gives a tidy figure in gibibytes that reads better on a 27-inch monitor than a raw integer with twelve digits.
The same line of code works whether the script lives on a Mac mini in Adelaide or a Linux tower in a Perth garage. That is one of the quiet joys of Python as a glue language, and the same philosophy that runs through projects like DenyHosts, where one script handles multiple Unix flavours without any fuss.
The real-time loop
For continuous monitoring, a simple while True loop with a time.sleep() pause is enough to get started. A one-second refresh feels good on the eyeballs, but if you are SSH-ed into a box over a patchy NBN link from a holiday house in Byron Bay, you might want to stretch that out to two or three seconds to keep the terminal responsive.
Inside the loop, the script grabs the memory snapshot, formats the numbers, and prints them with a carriage return rather than a newline. Using \r and flushing the stream with sys.stdout.flush() gives you that smooth updating line, the sort of thing you might see in htop or glances. Adding a small ANSI escape to clear the line before each update keeps the output crisp on embedded Linux boards where flicker can be annoying.
A more polished version can also write a timestamp to the line so you know when each reading came in. That is particularly useful when you are chasing an intermittent spike, like the kind that hits when a scheduled cron job runs at 2am AEST. Storing the timestamp in the local zone makes the log file easy to read back over a coffee the next morning.
Formatting the output for Australian devs
Raw bytes and percentages are fine, but humans like context. Wrapping the values in a small template that also shows the time, the hostname, and maybe a colour-coded warning when memory crosses a threshold turns the script from a curiosity into a genuine tool. Aussie sysadmins have a soft spot for terse, readable output, the kind you might jot down on the back of a coaster while waiting for your smashed avo.
A neat addition is a small bar made of hash characters that scales with the percentage used. Something like [####### ] 68% reads at a glance and does not depend on colour, which matters when you are piping the output into a log file on a server in a Sydney data centre. The bar can be wrapped in ANSI colours for terminal viewing, with green below 60 percent, amber between 60 and 85, and red beyond that, matching the thresholds most teams use for paging alerts.
For those who like to keep an eye on things while away from the desk, the script can push a line to a local logfile every minute, so you can graph it later in Grafana or just eyeball it in a spreadsheet. That is a comfortable middle ground between running free -m once and standing up a full Prometheus stack.
Extending it for real use
Once the core loop is solid, a few small touches make the script feel finished. Adding argument parsing with argparse lets the operator set the refresh interval, the warning threshold, and an optional logfile path from the command line, which suits the way most Aussie operators like to work: lean CLI tools, no clicks.
If the script needs to run unattended, dropping it into a systemd unit file with Restart=on-failure will keep it ticking along even after a reboot. For teams that already use Slack or Mattermost, a small push to a webhook when memory crosses a hard limit is a cheap way to get paged without standing up a whole monitoring suite. The same pattern works for emailing a duty engineer, though most teams out here prefer chat because nobody checks email at 3am unless they really have to.
Packaging the whole thing up so it can be installed with pip install . is a satisfying way to round out the project, and it fits nicely alongside the other utilities Phil has released. A short README that covers install, usage, and the usual licensing details will make it easy for the next person to pick up. Once that is done, you have a tiny piece of tooling that does one job well, the kind of thing that quietly earns its keep on every dev workstation from Darwin to Hobart.
