Open-source project
asdf-vm/asdf-elixir avatar
asdf-vm/asdf-elixir

asdf-elixir: installing Elixir versions with the asdf version manager

Elixir plugin for asdf version manager

527 stars79 forksShellMIT

At a glance

What is it?
asdf-elixir is the asdf plugin that installs and switches Elixir versions, and the interesting part is that it can install either precompiled binaries built by the hexpm Bob build server or a build from a specific Git reference, with an OTP suffix on the version to pick which Erlang release the binaries were built against. It suits an Elixir developer who wants several versions available side by side, and the detail to plan around is that Elixir and Erlang have to agree on versions.
Who is it for?
Adopt asdf-elixir if you work across more than one Elixir version, since asdf gives you per-project version selection through the .tool-versions file and the per-version shims.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 80 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the plugin does

asdf-elixir is the Elixir plugin for the asdf version manager, and the plugin model is worth understanding before anything else.

asdf is a version manager in the Unix tradition. It does not manage one tool. It provides a mechanism, and each tool gets a plugin that knows how to install, list and select that tool's versions. So the same `asdf` command works for Node, Ruby, Python and Elixir, and the plugin is the part that knows what an Elixir release tarball looks like and where to fetch it from.

The plugin also inherits asdf's shim mechanism. When a version is installed, asdf writes a small shim into a directory on your PATH for each executable that version provides, and the shim looks up which version is selected in the current directory and executes that. That is why the plugin has a `shims/` directory in its own repository and a `bin/` directory with its scripts.

The plugin is MIT licensed, the primary language is Shell, the default branch is master, and the last push was on 2026-08-05. There are no GitHub releases, which is the usual arrangement for an asdf plugin, since the plugin is fetched from the repository rather than downloaded as an artefact.

The repository itself is small: a `bin/` directory with the plugin's entry point, a `scripts/` directory with the install and download logic, the `shims/` directory, a `.shellcheckrc` and the README. The shellcheck configuration is a small sign of a project that lints its own scripts.

Installation itself is one command, after the plugin's one prerequisite is met:

code
asdf plugin add elixir https://github.com/asdf-vm/asdf-elixir.git

That command tells asdf where to find the plugin. The prerequisite is `unzip`, which the plugin needs because Elixir's release archives are distributed as zip files, and on a Debian system that is `sudo apt-get install unzip`.

Precompiled binaries and the OTP suffix

The plugin offers two ways to get an Elixir version, and the precompiled path is the one with a subtlety worth understanding.

Precompiled Elixir packages are built by Bob, the hexpm build server, whenever a git push or a new release is made at the Elixir repository. There is also a status page with an overview of all available packages and their availability.

The subtlety is what happens when you do not qualify the version. The precompiled packages are built against every officially supported OTP version, however, if you only specify the Elixir version, like `1.4.5`, the downloaded binaries will be those compiled against the oldest OTP release supported by that version.

So the default is the conservative choice. Asking for Elixir 1.4.5 gets you a build made for the oldest Erlang that Elixir 1.4.5 supports, which maximises compatibility but means you are not getting the benefits of a newer OTP.

If you would like to use precompiled binaries built with a more recent OTP, you can append `-otp-${OTP_MAJOR_VERSION}` to any installable version. The example given is that to install Elixir 1.5.0 and take advantage of the new features from OTP-20 you might install version `1.5.0-otp-20`.

That suffix is the whole mechanism, and it is worth understanding what it means. Elixir is compiled against Erlang's standard library and its behaviour, including error handling and the shape of some language features, follows the OTP version it was built with. So choosing the OTP suffix is choosing which Erlang behaviour you want baked into your Elixir installation.

The README gives a strong piece of advice about ordering. You do not need to have Erlang installed to install a precompiled version of Elixir, but you will need to have it available at runtime, otherwise your Elixir commands will fail. Even though it is possible to install Elixir first, it is recommended to install Erlang first.

That is a practical ordering constraint rather than a technical one, and it comes from the same concern as the compatibility table. Elixir requires Erlang to be installed, and the plugin points at the asdf-erlang plugin for installing Erlang versions. You also need to be sure to install the corresponding Erlang/OTP version with asdf-erlang and to have both selected versions in your `.tool-versions` file.

That last requirement is the one that ties the two plugins together. The `.tool-versions` file records both, so a project pins the Elixir version and the Erlang version together, and the two are always selected as a pair.

Building from a Git reference

The second installation path is building from a Git reference, and it exists for two reasons: you may want a commit that was never released, or you may want your own modifications to the source.

The CLI form installs a specific commit reference from the Elixir GitHub repository:

bash
# install in the entire environment
asdf set --home elixir ref-<commit/tag reference>

# Or install locally in project
asdf set elixir ref-<commit/tag reference>

The `ref:` prefix is what tells the plugin that this is not a version number but a Git reference, and the plugin fetches the source and compiles it. The distinction between `--home` and the local form is the asdf convention: `--home` sets it globally for the user, the local form sets it for the current project only.

You can also compile Elixir from source without using asdf at all, for example so that you can use the master branch of Elixir or a branch with your own modifications, then use it by specifying the directory path:

code
# After Elixir already installed into /path/to/elixir
asdf set elixir path:/path/to/elixir
# Or
asdf set --home elixir path:/path/to/elixir

So there are three sources in total: a numbered release, a Git reference, and a directory you compiled yourself. The path form is the most useful of the three for development, because it lets you point at a working tree and rebuild it.

The `.tool-versions` file accepts the same two non-numeric forms:

code
elixir ref:<commit reference>

Or if you've already compiled Elixir from source in a specific directory:

code
elixir path:/path/to/elixir

And there is a constraint on the path form that is easy to get wrong. The path specified must be an absolute path to the Elixir installation's root directory, for example the directory containing the `bin` directory.

That is a root directory and not the bin directory itself, which is a distinction that matters because asdf shims expect to find the version's executables under a predictable relative path. A relative path is also not accepted, since the `.tool-versions` file is read from whatever directory you are in.

The compatibility question is answered by a link to the hexdocs page on compatibility and deprecations between Elixir and Erlang OTP, which is the reference to check before committing a pair of versions to a project file.

Escripts and the default mix commands

Two smaller features round out the plugin, and both exist because asdf's model has consequences for tools installed alongside a language.

The first is escript support. The plugin supports Elixir escripts, adding them to your path just like any other Elixir binary. Whenever you install a new escript with `mix escript.install` you need to run `asdf reshim elixir` in order to create shims for it.

That requirement is a direct consequence of how asdf works, and it is worth understanding rather than memorising. asdf does not scan your PATH or your installation directory to find executables. It writes a shim for each executable it knows about, at install time, and the set of shims does not change when you install something else into the same version directory. So a newly added escript has no shim until you reshim.

This catches people out regularly, and it applies to anything installed after the fact, not only escripts. The reshim command is the general answer.

The second feature is the default mix commands. After installing you can specify which additional mix commands must be run after adding a new Elixir version. The README says this can be particularly useful for installing often used archives. They need to be specified in `$HOME/.default-mix-commands`, the flag `--force` will be added by default.

The example given is:

code
local.hex
local.rebar
archive.install hex phx_new

The first two install the Hex package manager and the Erlang build tool Rebar, both of which are needed for ordinary Elixir project work and neither of which ships with Elixir itself. The third installs the Phoenix new-project generator, an archive rather than a binary.

Running these automatically on every version install is a real convenience, because otherwise you would run them once per version, and the first `mix deps.get` in a new project would fail on a fresh version until you remembered.

The `--force` flag being added by default is the detail that makes it work. These commands install things into your user home directory rather than into the Elixir installation, so without forcing, a second version install would fail because the archive was already installed. Forcing makes the command idempotent in effect, and it is the difference between the feature working across versions and working only for the first one.

The README closes by pointing at the asdf README for the general instructions on installing and managing versions, and at a blog post on using asdf with Elixir and Phoenix for a walkthrough of the whole setup.

Where the plugin fits in the asdf ecosystem

The plugin sits in a wider ecosystem, and knowing what sits next to it makes the README's advice easier to follow.

The homepage for this project is the asdf repository itself, and the README ends by pointing at the asdf readme for instructions on how to install and manage versions of Elixir. That is deliberate. The plugin does not try to be a self-contained installer. It assumes asdf is installed and configured, that the shim directory is on your PATH, and that you understand the concepts of a local version and a global version, and it fills in only the part that is Elixir-specific.

The asdf-erlang plugin is the counterpart, and the two are designed to be used together. Erlang is a separate project with its own release cycle and its own compatibility rules, so it gets its own plugin, and the `.tool-versions` file is what binds them. A project file listing both an elixir line and an erlang line pins both, and asdf resolves them together when you enter the directory.

That arrangement is what makes the OTP suffix work in practice. The suffix on the Elixir version says which Erlang behaviour the binaries were built with, and the Erlang line in the project file says which Erlang is installed. If the two disagree, you have an Elixir compiled for one OTP running on another, which is exactly the class of problem the compatibility table exists to prevent.

The last thing the README offers is a walkthrough rather than a specification. There is a link to a blog post on using asdf with Elixir and Phoenix, which is useful for anyone setting this up for the first time, since the combination of three moving parts, asdf itself, the two language plugins and the project version file, is more than the individual command lines suggest.

So the practical sequence for a new machine is: install asdf, add the elixir plugin, add the erlang plugin, install the Erlang version you want, then install the matching Elixir version, and record both in the project's `.tool-versions` file. The README's ordering advice about installing Erlang first follows directly from that sequence.

Editorial conclusion

Adopt asdf-elixir if you work across more than one Elixir version, since asdf gives you per-project version selection through the .tool-versions file and the per-version shims. Do not install Elixir without checking the compatibility table between Elixir and Erlang OTP first, because a mismatched pair compiles and then fails at run time, and check first which Erlang version you want when picking a precompiled build, since the default picks the oldest supported OTP rather than the newest.

Frequently asked questions

What is asdf-elixir?

It is the Elixir plugin for the asdf version manager, a MIT licensed shell project that installs and selects Elixir versions alongside the other tools asdf manages. The repository has a bin directory with the plugin entry point, a scripts directory with the install logic and a shims directory, and it requires unzip because Elixir release archives are zip files.

How do I install asdf-elixir?

First make sure unzip is present, which on a Debian system means `sudo apt-get install unzip`. Then run `asdf plugin add elixir https://github.com/asdf-vm/asdf-elixir.git`. Elixir also requires Erlang, and the README recommends installing that first with the asdf-erlang plugin.

How do I get a precompiled Elixir build against a specific OTP release?

Append `-otp-${OTP_MAJOR_VERSION}` to the version, for example `1.5.0-otp-20`. Without the suffix you get the binaries compiled against the oldest OTP release supported by that Elixir version. You then need the corresponding Erlang version installed through asdf-erlang with both versions selected in your .tool-versions file.

Can asdf-elixir install Elixir from a Git commit?

Yes. Run `asdf install elixir ref:<commit reference>` to build a specific commit, then select it with `asdf set elixir ref-<commit/tag reference>` for the current project or `asdf set --home elixir ref-<commit/tag reference>` for the whole environment. You can also point at a directory you compiled yourself with `asdf set elixir path:/path/to/elixir`, where the path must be absolute and point at the installation root containing the bin directory.

Why do I need to run asdf reshim after installing an escript?

Because asdf writes a shim for each executable it knows about when a version is installed, and it does not rescan the installation directory afterwards. A new escript added with `mix escript.install` therefore has no shim until you run `asdf reshim elixir`.

How do I make mix commands run automatically on each Elixir install?

Put them in `$HOME/.default-mix-commands`, one per line, for example `local.hex`, `local.rebar` and `archive.install hex phx_new`. The flag --force is added by default, which lets the same commands run again for each new version without failing over an already installed archive.

Official sources

  1. asdf-vm/asdf-elixir on GitHub
  2. Issues
  3. License: MIT
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/asdf-vm-asdf-elixir.svg)](https://hysenlabs.com/projects/asdf-vm-asdf-elixir)

Was this useful?