Skip to main content

beamruntime

· 6 min read
João Henrique Ferreira de Freitas
Maintainer of meta-erlang

One of the coolest features from the Yocto Project is the ability of creating toolchains and SDK for specific needs. There is a full session on Yocto's documentation about Software Development Kits. Also, many talks showing that feature in action, e.g.: Building Bare Metal Toolchains, Crosstool-ng and Yocto Project - Mark Hatle, Xilinx.

While reading this discussion: It is possible that this project may expand to also provide Linux builds?, I thought it would be interesting as a PoC to create a sort of beamruntime Yocto based toolchain with only Erlang/OTP and Elixir installed plus base libraries like libc, termcap, openssl. Nothing more. As meta-erlang layer has all the components and infrastructure for that. See beamtools idea.

The target is for Linux boxes that does not have Erlang/OTP installed, then the user would install beamruntime and get access to a fresh and up to date Erlang/OTP and Elixir environment. As the SDK generated by Yocto is self-content, there is no need to install anything else into Operational System.

Moreover, beamruntime is linux distribution independent, it could run in any distribution for the target architecture.

In fact, this idea is not new in meta-erlang land, beamtools uses exact the same approach. The difference is beamtools has development tools for building Erlang/OTP and Elixir application (like headers, rebar3, etc). While beamruntime is just for running application, that is BEAM VM plus and modules.

beamruntime recipe

I've started with two architecture targets in mind: x86-64 and aarch64. And a new bitbake recipe called beamruntime-tarball.

After building it using bitbake beamruntime-tarball a sdk was made and available at tmp/deploy/sdk ready for use.

I borrowed some ideas from the OE-core recipe from here. Because the purpose is very similar.

Practical usage

Installing beamruntime is like running a selfextract shell script which install beamruntime in the target folder (-d option):

$ x86_64-beamruntime-nativesdk-standalone-5.2.99+snapshot-musl-erlang-28.0.4-elixir-1.18.4.sh -d /tmp/test0
BEAM runtime installer version 5.2.99+snapshot-erlang-28.0.4-elixir-1.18.4
==========================================================================
You are about to install the SDK to "/tmp/test0". Proceed [Y/n]?
Extracting SDK......done
Setting it up...done
SDK has been successfully set up and is ready to be used.
Each time you wish to use the SDK in a new shell session, you need to source the environment setup script e.g.
$ . /tmp/test0/environment-setup-x86_64-pokysdk-linux

After the installation, and for each shell, it's necessary to source a script that will fix some environment variables:

$ source /tmp/test0/environment-setup-x86_64-pokysdk-linux

$ erl

Erlang/OTP 28 [erts-16.0.3] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit:ns]

Eshell V16.0.3 (press Ctrl+G to abort, type help(). for help)
1> application:start(crypto).
ok
2>

Erlang shell is working as expected. Let's take a look in the image process mapping to check from where the beam PID is getting libraries:

$ cat /proc/142729/maps
60245d000000-60245d018000 rw-p 00a00000 fc:00 26099460 /tmp/test0/sysroots/x86_64-pokysdk-linux/usr/lib/erlang/erts-16.0.3/bin/beam.smp
...
785f53fee000-785f53ff3000 rw-p 001de000 fc:00 26099940 /tmp/test0/sysroots/x86_64-pokysdk-linux/lib/libc.so.6
...
785f54272000-785f54275000 rw-p 00271000 fc:00 20982895 /tmp/test0/sysroots/x86_64-pokysdk-linux/usr/lib/libstdc++.so.6.0.34
...
785f542cf000-785f542d0000 rw-p 0002b000 fc:00 26099910 /tmp/test0/sysroots/x86_64-pokysdk-linux/lib/libgcc_s.so.1
...
785f543c1000-785f543c2000 rw-p 000f0000 fc:00 26099961 /tmp/test0/sysroots/x86_64-pokysdk-linux/lib/libm.so.6
...
785f543dc000-785f543dd000 rw-p 00019000 fc:00 20982885 /tmp/test0/sysroots/x86_64-pokysdk-linux/usr/lib/libz.so.1.3.1
...
785f5440f000-785f54410000 rw-p 00031000 fc:00 26099959 /tmp/test0/sysroots/x86_64-pokysdk-linux/lib/libtinfo.so.5.9
...
785f54451000-785f54452000 rw-p 00039000 fc:00 26099952 /tmp/test0/sysroots/x86_64-pokysdk-linux/lib/ld-linux-x86-64.so.2

As expected every dependency is self-content from /tmp/test0. Even the openssl libs:

Get some crypto info:

2> crypto:info().
#{otp_crypto_version => "5.6",compile_type => normal,
link_type => dynamic,
cryptolib_version_compiled => "OpenSSL 3.5.2 5 Aug 2025",
cryptolib_version_linked => "OpenSSL 3.5.2 5 Aug 2025",
fips_provider_available => false}
3> crypto:info_lib().
[{<<"OpenSSL">>,810549280,<<"OpenSSL 3.5.2 5 Aug 2025">>}]

My Ubuntu (24.04) host has OpenSSL 3.0.13, not 3.5.2. That is a good sign for self-content approach.

So, beamruntime could be used as a generic Erlang/OTP and Elixir installation for Linux boxes.

More details

The file x86_64-beamruntime-nativesdk-standalone-5.2.99+snapshot-musl-erlang-28.0.4-elixir-1.18.4.sh has 36291 KB and when installed it goes up to 78476 KB. The user can install it in any folder.

note

The size footprint is relative, as in this experience I was targeting a generic installation.

beamruntime provides the following packages:

  • libc
  • terminfo
  • zlib
  • openssl
  • Erlang/OTP
  • Elixir

A more detailed list with package size and theirs names:

11825	KiB	nativesdk-erlang-erts
7018 KiB nativesdk-elixir
6418 KiB nativesdk-erlang-stdlib
5894 KiB nativesdk-libcrypto3
4428 KiB nativesdk-libc6
3248 KiB nativesdk-erlang-compiler
3163 KiB nativesdk-erlang-snmp
2890 KiB nativesdk-glibc-binary-localedata-en-us.utf-8
2868 KiB nativesdk-erlang-kernel
2681 KiB nativesdk-erlang-public-key
2511 KiB nativesdk-libstdc++6
2254 KiB nativesdk-erlang-ssl
1971 KiB nativesdk-erlang-xmerl
1893 KiB nativesdk-erlang-common-test
1459 KiB nativesdk-erlang-dialyzer
1267 KiB nativesdk-erlang-ssh
1246 KiB nativesdk-erlang-asn1
1158 KiB nativesdk-erlang-mnesia
1137 KiB nativesdk-erlang-diameter
848 KiB nativesdk-erlang-inets
699 KiB nativesdk-erlang-tools
594 KiB nativesdk-erlang-edoc
472 KiB nativesdk-erlang-emacs
467 KiB nativesdk-erlang-reltool
449 KiB nativesdk-erlang-syntax-tools
401 KiB nativesdk-erlang-sasl
348 KiB nativesdk-erlang-crypto
328 KiB nativesdk-erlang-runtime-tools
261 KiB nativesdk-erlang-parsetools
220 KiB nativesdk-erlang-eunit
200 KiB nativesdk-libtinfo5
177 KiB nativesdk-erlang-eldap
175 KiB nativesdk-libgcc1
159 KiB nativesdk-erlang-os-mon
139 KiB nativesdk-openssl-ossl-module-legacy
124 KiB nativesdk-erlang-erl-interface
123 KiB nativesdk-erlang-tftp
115 KiB nativesdk-erlang-ftp
102 KiB nativesdk-libz1
85 KiB nativesdk-erlang-typer
66 KiB nativesdk-erlang-epmd
26 KiB nativesdk-ncurses-terminfo-base
24 KiB nativesdk-openssl-conf
0 KiB nativesdk-sdk-provides-dummy
0 KiB nativesdk-erlang-modules
0 KiB nativesdk-erlang-erl-docgen
0 KiB nativesdk-erlang

On this Erlang/OTP installation, I did not enabled wx (wxwidget), odbc, lkscp options. And all Erlang/OTP modules have been installed. It's a pretty generic installation, though.

Some additional Yocto configuration for reducing size were applied. Like the following:

  • Disable locales:

    ENABLE_BINARY_LOCALE_GENERATION       = "1"
    LOCALE_GENERATION_WITH_CROSS_LOCALEDEF = "1"
    GLIBC_INTERNAL_USE_BINARY_LOCALE = "compile"

    GLIBC_GENERATE_LOCALES = "en_US.UTF-8"
    IMAGE_LINGUAS = "en-us"

    LOCALE_UTF8_ONLY = "1"
    LOCALE_UTF8_IS_DEFAULT = "1"
  • Use musl:

    TCLIBC = "musl"
  • Disable opengl support:

    DISTRO_FEATURES_FILTER_NATIVESDK:remove = "opengl"
  • Optional: disable termcap (reducing ~300KB)

    PACKAGECONFIG:remove:pn-nativesdk-erlang = "termcap"

Next steps and ideas

  • Provide musl and glibc beamruntime packages for testing. Both versions of beamruntime work as expected.
  • Enable static build, what would be the final footprint ?
  • Make beamruntime a bit more generic like adding odbc, lksctp ?
  • Or make it less generic removing some erlang application that does not make sense for runtime. Like: dialyzer, edoc, emacs, typer
  • What does mean generic nowadays ? Providing a generic beamruntime is feasible and if a specific user wants to add or remove components it could be easily done building a beamruntime itself.
  • How beamruntime could be useful for tools that expect a Erlang/OTP installation on the target host ?