waseigo

waseigo

DiskSpace - retrieve disk usage statistics for a given filesystem path

DiskSpace

Hi everyone, I needed this for a project I’ve been working on, and couldn’t find a simple solution that would work. The solutions in Check disk space inside elixir didn’t work for me.

Looking at how Exqlite deals with NIFs, I wrote a Makefile and then used Grok 3 with numerous rounds to generate the C file for a NIF. I then had GPT-5 and Gemini 2.5 Flash review it, then back to Grok 3 for implementing the suggested improvements, as Grok’s license permits licensing generated code permissively. Back and forth, again and again.

It should work on everything POSIX and on Windows, though I have only tested it on Debian 12. I don’t have Apple computers, and currently don’t run FreeBSD or NetBSD anywhere to try it out there.

What it does

It returns information about total, used, free, and available disk space, using native system calls for accuracy and performance. Optionally converts the results into human-readable strings with kibibytes etc. or kilobytes etc.

Returns disk space metrics as a map with keys:

  • :total — total size of the filesystem
  • :used — bytes currently used
  • :free — bytes free on the filesystem
  • :available — bytes available to the current user (may be less than free due to permissions)

Provides both safe (stat/2) and bang (stat!/2) variants (with opts keyword-list options for human-readable output), the latter raising on errors.

Provides a humanize/2 function to which you can pipe the output of stat/2 and stat!/2 to generate human-readable output, if you don’t want to pass opts to those functions.

Usage example

iex(1)> DiskSpace.stat!("/tmp")
%{
  available: 32389640192,
  free: 43895349248,
  total: 225035927552,
  used: 181140578304
}
iex(2)> DiskSpace.stat("/tmp")
{:ok,
 %{
   available: 32389599232,
   free: 43895308288,
   total: 225035927552,
   used: 181140619264
 }}
iex(3)> DiskSpace.stat("/tmp", humanize: true)
{:ok,
 %{
   available: "30.17 GiB",
   free: "40.88 GiB",
   total: "209.58 GiB",
   used: "168.70 GiB"
 }}
iex(4)> DiskSpace.stat("/tmp", humanize: true, base: :decimal)
{:ok,
 %{
   available: "32.39 GB",
   free: "43.90 GB",
   total: "225.04 GB",
   used: "181.14 GB"
 }}
iex(5)> DiskSpace.stat!("/yolo/swag")
** (DiskSpace.Error) DiskSpace error: :realpath_failed
    (disk_space 0.1.0) lib/disk_space.ex:86: DiskSpace.stat!/2
    iex:5: (file)

Error-handling

  • stat/2 returns {:ok, stats_map} or {:error, reason}
  • stat!/2 returns stats_map or raises DiskSpace.Error

Reporting issues and opening PRs welcome.

Most Liked

NobbZ

NobbZ

Of course there is a runtime requirement… You have to add an entry to the applications deps list, and unless you set runtime: false your app will be loaded and started at runtime.

Please be aware that this is implicit, and compare it to the explicit adding to applications back before elixir 1.5.

waseigo

waseigo

Actually, I just got disksup working. I had to add :os_mon to the :extra_applications in mix.exs… Oh well, live and learn!

To answer your question:

Criterion :disksup.get_disk_info/1 disk_space.stat/2 and stat!/2
What it is Function of a supervised process (:os_mon’s :disksup) Function relying on a NIF
Runtime requirements :os_mon in :extra_applications in mix.exs Nothing
Need to compile? No, part of Erlang Yes
Returns Returns total space, available space, and capacity (% of disk space used) Returns total space, used space, free space, available to the current user
Return value type 4-element tuple in list; first element: path as charlist; other elements: integers {:ok, stats_map} or {:error, reason} (stat/2), stats_map or raises exception (stat!/2), where stats_map is a plain Elixir map with atom keys with: integer values (bytes) if option :humanize is false (default), string values (KiB, kB, etc.) if :humanize is true
Return units kibibytes, percentage (as integers) bytes (integers) or human-readable strings with humanize: true, either as KiB etc. (base: :binary, default) or kB etc. (base: :decimal)
Interval 30 minutes (default), configurable with disk_space_check_interval N/A - checks upon invocation
Optional conversion to KiB, kB, etc. No Yes, with DiskSpace.humanize/2
Works with UNCs on Windows? Maybe not (“On WIN32 - All logical drives of type “FIXED_DISK” are checked.”) Should work (not tested / cannot test)
Can alert you? Yes, with :alarm_handler, signal :disk_almost_full, via :os_mon No, use it for “spot checks”
Well tested? Yes No (not yet)
waseigo

waseigo

Version 0.3.0 is up on Hex.pm.

  • Tests now do not rely on mocking
  • Requires OTP 27 on Windows
  • Requires OTP 26 and above on recent Linux and on macOS
  • Works on NetBSD 10.1, FreeBSD 14.3 and OpenBSD 7.7
  • Does not work on DragonFlyBSD 6.4.1 due to OTP 25
  • GitHub Actions workflow added for Linux, macOS, Windows

Supported Elixir and OTP versions

OS Arch. Elixir OTP Builds and mix test passes?
Linux (Ubuntu/Debian) amd64 1.14 25 :cross_mark: errors with Erlang headers
Linux (Ubuntu/Debian) amd64 1.15 26 :white_check_mark:
Linux (Ubuntu/Debian) amd64 1.16 26 :white_check_mark:
Linux (Ubuntu/Debian) amd64 1.17 27 :white_check_mark:
Linux (Ubuntu/Debian) amd64 1.18 27 :white_check_mark:
Linux (Ubuntu/Debian) amd64 1.18.4 28 :white_question_mark: Unknown / not tested
macOS arm64 1.14 25 :cross_mark: errors with Erlang headers
macOS arm64 1.15 26 :white_check_mark:
macOS arm64 1.16 26 :white_check_mark:
macOS arm64 1.17 27 :white_check_mark:
macOS arm64 1.18 27 :white_check_mark:
macOS arm64 1.18.4 28 :white_question_mark: Unknown / not tested
Windows amd64 1.14 25 :cross_mark: errors with Erlang headers
Windows amd64 1.15 26 :cross_mark: errors with Erlang headers
Windows amd64 1.16 26 :cross_mark: errors with Erlang headers
Windows amd64 1.17 27 :white_check_mark:
Windows amd64 1.18 27 :white_check_mark:
Windows amd64 1.18.4 28 :white_question_mark: Unknown / not tested
NetBSD 10.1 amd64 1.17.2 27 :white_check_mark:
FreeBSD 14.3 amd64 1.17.3 26 :white_check_mark:
OpenBSD 7.7 amd64 1.18.3 27 :white_check_mark:
DragonFlyBSD 6.4.2 amd64 1.16.3 25 :cross_mark: errors with Erlang headers

Apologies to those behind the 40+ downloads of the prior versions, for whom it was only working on Linux. First time I’m dealing with a NIF (or GitHub Actions, for that matter)!

Where Next?

Popular in Announcing Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
Flo0807
Hello everyone! I am excited to share our heart project Backpex with you. After building several Phoenix applications, we realized that...
New
BartOtten
Phoenix Live Favicon Favicon manipulation for Phoenix Live A lib enabling dynamic favicons in Phoenix Live View applications. To sho...
New
pcharbon
This is a new extension for the Ash framework that lets you use the Commanded library in a more declarative manner and removes most of th...
New
mikehostetler
Hey everyone! I’m excited to share ReqLLM - a new approach to LLM interactions in Elixir that I’ve been working on. After building agent...
New
bradley
I’ve been working with Claude Code extensively and absolutely love it. However, I’ve come across the challenge of managing configuration ...
New
garrison
Hobbes is a scalable, fault-tolerant transactional record store written in Elixir. Hobbes is designed to be: Scalable - Hobbes can sha...
New
lostbean
Hello Elixir community! :wave: I’m excited to announce the first release of ReqCassette, a VCR-style record-and-replay library specifica...
New
leandrocp
MDEx is a fast and extensible Markdown parser and formatter. Fast Leverage Rust to parse, manipulate and render documents using: comra...
New
sodapopcan
I’ve heard that if you’re not embarrassed by v1 of your product Vim plugin then you’ve released too late. I’ve been sitting on this the ...
New

Other popular topics Top

JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1140 51847 244
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

We're in Beta

About us Mission Statement