DiskSpace

A small Elixir library with a NIF in Rust for getting disk usage statistics for a given filesystem path.

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

Features

  • 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) functions, the latter raising DiskSpace.Error on errors
  • Optional conversion of results from bytes into human-readable strings (in kB, KiB, etc.) with a keyword-list option that calls humanize/2
  • Supports Linux, macOS, Windows, NetBSD, FreeBSD, OpenBSD, DragonFlyBSD

Installation

Add disk_space to your list of dependencies in mix.exs:

def deps do
  [
    {:disk_space, "~> 1.0.0"}
  ]
end

Usage examples

iex(1)> DiskSpace.stat!("/tmp")
%{
  free: 39917449216,
  total: 225035927552,
  used: 185118478336,
  available: 28411740160
}
iex(2)> DiskSpace.stat("/tmp")
{:ok,
 %{
   free: 39917436928,
   total: 225035927552,
   used: 185118490624,
   available: 28411727872
 }}
iex(3)> DiskSpace.stat("/tmp", humanize: :binary)
{:ok,
 %{
   free: "37.18 GiB",
   total: "209.58 GiB",
   used: "172.41 GiB",
   available: "26.46 GiB"
 }}
iex(4)> DiskSpace.stat("/tmp", humanize: :decimal)
{:ok,
 %{
   free: "39.92 GB",
   total: "225.04 GB",
   used: "185.12 GB",
   available: "28.41 GB"
 }}
 iex(5)> DiskSpace.stat("/home/tisaak") |> DiskSpace.humanize()
{:ok,
 %{
   free: "37.18 GiB",
   total: "209.58 GiB",
   used: "172.41 GiB",
   available: "26.46 GiB"
 }}
iex(6)> DiskSpace.stat("/home/tisaak") |> DiskSpace.humanize(:decimal)
{:ok,
 %{
   free: "39.92 GB",
   total: "225.04 GB",
   used: "185.12 GB",
   available: "28.41 GB"
 }}
iex(7)> DiskSpace.stat("/yolo/swag")
{:error,
 %{
   info: %{errno: 2, errstr: "No such file or directory (os error 2)"},
   reason: :not_directory
 }}
iex(8)> DiskSpace.stat!("/yolo/swag")
** (DiskSpace.Error) DiskSpace error: %{info: %{errno: 2, errstr: "No such file or directory (os error 2)"}, reason: :not_directory}
    (disk_space 1.0.0) lib/disk_space.ex:84: DiskSpace.stat!/2
    iex:8: (file)

Usage trick

In case you want to get results for a path that doesn't yet exist:

  def recursively_check_disk_space(local_dir) when is_binary(local_dir) do
    local_dir |> Path.split() |> Enum.reduce_while(local_dir, fn _, acc ->
      case DiskSpace.stat(acc) do
        {:ok, info} -> {:halt, {:ok, info}}
        {:error, _} ->
          {:cont, acc |> Path.split() |> Enum.reverse() |> tl |> Enum.reverse() |> Path.join()}
      end
    end)
  end

This is pulled from my book Elixir File Browsing, in which the API client for the undocumented REST API of File Browser uses DiskSpace to check whether there is enough space on the target local path's mount point before downloading a resource from the server.

Error handling

  • stat/2 returns {:ok, stats_map} or {:error, info}, where info is a map with populated :reason (atom) and :info (map or nil) with more information, if provided by the NIF.
  • stat!/2 returns stats_map or raises DiskSpace.Error with the {:error, info} of stat/2 as the message.

Supported Elixir and OTP versions

In short:

  • Tested and confirmed working on Elixir 1.14 (OTP 25) to 1.18 (OTP 27)
  • Tested and confirmed working on Linux, Windows and the BSDs (amd64)
  • Tested and confirmed working on macOS (arm64)
  • Reported as also working on Elixir 1.18.4 (OTP 28), at least on macOS/arm64

Build & test matrix

OSArch.ElixirOTPBuilds and mix test passes?
Linux (Ubuntu/Debian)amd641.1425
Linux (Ubuntu/Debian)amd641.1526
Linux (Ubuntu/Debian)amd641.1626
Linux (Ubuntu/Debian)amd641.1727
Linux (Ubuntu/Debian)amd641.1827
Linux (Ubuntu/Debian)amd641.18.428❔ Not tested, but should work
macOSarm641.1425
macOSarm641.1526
macOSarm641.1626
macOSarm641.1727
macOSarm641.1827
macOSarm641.18.428✅ reported as working
Windowsamd641.1425
Windowsamd641.1526
Windowsamd641.1626
Windowsamd641.1727
Windowsamd641.1827
Windowsamd641.18.428❔ Not tested, but should work
NetBSD 10.1amd641.17.227
FreeBSD 14.3amd641.17.326
OpenBSD 7.7amd641.18.327
DragonFlyBSD 6.4.2amd641.16.325

See also: GitHub Actions for Linux, macOS, Windows.

Build requirements

Generally: Erlang development headers (for erl_nif functions), Rust.

Linux (amd64)

  • erlang-dev or erlang-erts-dev (Erlang development headers)
  • libc development headers (usually installed by default)
  • rustc

Example on Debian and its derivatives:

sudo apt-get install elixir erlang-dev rustc

macOS (arm64)

xcode-select --install
  • clang
  • Erlang installed via Homebrew or other means

NetBSD (amd64)

✅ Tested on version 10.1 with Elixir 1.17.2, OTP 27.

pkgin update
pkgin install erlang elixir rust

FreeBSD (amd64)

✅ Tested on version 14.3 with Elixir 1.17.3, OTP 26.

pkg update
pkg install erlang elixir ca_root_nss rust

DragonFlyBSD (amd64)

✅ Tested on version 6.4.2 with Elixir 1.16.3, OTP 25.

pkg update
pkg install erlang elixir rust

OpenBSD (amd64)

✅ Tested on version 7.7 with Elixir 1.18.3, OTP 27.

pkg_add erlang-27.3.3v0 elixir-1.18.3 rust

Windows (amd64)

  1. Install Erlang/OTP with development headers:

    • Download the official installer from https://www.erlang.org/downloads (choose the latest stable version).
    • During installation, ensure "Development and debugging tools" is selected (this includes headers like erl_nif.h).
    • Add the Erlang bin directory to your PATH (e.g., C:\Program Files\erl-27.0\bin).
  2. Install Elixir:

  3. Install Visual Studio Build Tools (required for Rust's MSVC toolchain):

    • Download from https://visualstudio.microsoft.com/downloads/ (under "Tools for Visual Studio", select "Build Tools for Visual Studio").
    • Run the installer and select the "C++ build tools" workload (includes MSVC compiler and linker).
    • No full Visual Studio IDE is needed—just the build tools.
  4. Install Rust:

    • Download rustup-init.exe from https://www.rust-lang.org/tools/install.
    • Run it and select the default options, which install the stable MSVC toolchain (stable-x86_64-pc-windows-msvc).
    • Add Rust to your PATH if prompted (cargo and rustc should be accessible from the command line).

Alternatives

Add :os_mon in :extra_applications in mix.exs, then use get_disk_info/1 of disksup service.

Comparison to alternatives

Criterion:disksup.get_disk_info/1disk_space.stat/2 and stat!/2
What it isFunction of a supervised process (:os_mon's disksup)Function relying on a NIF
Runtime requirements:os_mon in :extra_applications in mix.exsNone
Compile-time requirementsNo, part of Erlang/OTPYes (Rust)
ReturnsTotal space, available space, and capacity (% of disk space used)Returns total, used, free, available space
Return value type4-element tuple in list; first element: path as charlist; other elements: integers2-element tagged tuple; first element: :ok or :error; second element: map with atom keys and integer (bytes) or string (kB, KiB, etc.) values if :ok, map with :reason and OS :info if :error
Return unitskibibytes, percentage (as integers)bytes (integers) or human-readable strings
Optional conversion to KiB, kB, etc.NoYes (through humanize/2)
Works with UNCs on Windows?Probably not ("On WIN32 - All logical drives of type "FIXED_DISK" are checked.")Should work (not tested / cannot test)
Well tested?YesYes, according to GitHub Actions

Use of GenAI

The following files were incrementally generated/adapted by xAI's Grok 4 model over multiple rounds of prompting for reviews and improvements that were suggested by Grok 4, GPT-5 and Gemini 2.5 Pro, and according to the warnings/errors of the GitHub Actions workflow across Linux, macOS, and Windows:

License

Apache-2.0

Documentation

For more details, see the documentation at https://hexdocs.pm/disk_space.