gleam.build provides a comprehensive build function for Gleam projects that handles dependency fetching from Hex and Git, supports local packages, and can target either Erlang or JavaScript.
Overview
Build Gleam projects with automatic dependency resolution:
packages . myapp = lib . gleam . build {
src = ./. ;
} ;
Function Signature
gleam . build : : {
src ,
nativeBuildInputs ?,
localPackages ?,
erlangPackage ?,
rebar3Package ?,
target ?,
...
} -> Derivation
Source directory containing gleam.toml and manifest.toml.
Additional build inputs beyond the defaults.
List of local Gleam packages (as paths) to use instead of fetching from Hex. Used for monorepo setups.
erlangPackage
derivation
default: "pkgs.erlang"
Erlang runtime to use (only for Erlang target).
rebar3Package
derivation
default: "pkgs.rebar3"
Rebar3 build tool (only for Erlang target).
target
string
default: "from gleam.toml"
Build target: "erlang" or "javascript". Defaults to the target field in gleam.toml, or "erlang" if not specified.
Return Value
Returns a derivation with:
Binary in $out/bin/<name> (where <name> comes from gleam.toml)
For Erlang target: All BEAM files and dependencies in $out/lib/
For JavaScript target: All JS modules in $out/lib/
Examples
Basic Erlang Project
packages . myapp = lib . gleam . build {
src = ./. ;
} ;
With this gleam.toml:
name = "myapp"
version = "1.0.0"
target = "erlang"
[ dependencies ]
gleam_stdlib = "~> 0.34"
gleam_erlang = "~> 0.25"
JavaScript Target
packages . myapp-js = lib . gleam . build {
src = ./. ;
target = "javascript" ;
} ;
Or set in gleam.toml:
name = "myapp"
version = "1.0.0"
target = "javascript"
With Local Packages (Monorepo)
let
# Build shared library
shared = lib . gleam . build {
src = ./packages/shared ;
};
in
{
# Build main app with local dependency
packages . app = lib . gleam . build {
src = ./packages/app ;
localPackages = [ ./packages/shared ];
};
}
In packages/app/gleam.toml:
name = "app"
[ dependencies ]
shared = { path = "../shared" }
And in packages/app/manifest.toml:
packages = [
{ name = "shared" , source = "local" , path = "../shared" },
]
Custom Erlang Version
packages . myapp = lib . gleam . build {
src = ./. ;
erlangPackage = pkgs . erlang_26 ;
} ;
packages . myapp = lib . gleam . build {
src = ./. ;
nativeBuildInputs = [
pkgs . postgresql # For PostgreSQL driver FFI
];
} ;
Multi-Target Build
{
outputs = { self , nixpkgs , nur }:
nur . lib . mkFlake { } ( system :
let
pkgs = nixpkgs . legacyPackages . ${ system } ;
lib = nur . lib . ${ system } ;
in
{
packages = {
# Erlang target
default = lib . gleam . build {
src = ./. ;
target = "erlang" ;
};
# JavaScript target
js = lib . gleam . build {
src = ./. ;
target = "javascript" ;
};
};
}
);
}
Dependency Management
Hex Dependencies
Dependencies from Hex (the package manager) are automatically fetched based on manifest.toml:
packages = [
{ name = "gleam_stdlib" , version = "0.34.0" , source = "hex" , outer_checksum = "abc..." },
{ name = "gleam_json" , version = "1.0.0" , source = "hex" , outer_checksum = "def..." },
]
The outer_checksum is used by Nix’s fetchHex to verify downloads.
Git Dependencies
Git dependencies are also supported:
packages = [
{
name = "my_lib" ,
source = "git" ,
repo = "https://github.com/user/my_lib" ,
commit = "abc123..."
},
]
Local Dependencies
For monorepo setups:
packages = [
{ name = "shared" , source = "local" , path = "../shared" },
]
Then provide the actual path in Nix:
lib . gleam . build {
src = ./packages/app ;
localPackages = [ ./packages/shared ];
}
The build function will automatically patch manifest.toml and gleam.toml to use the Nix store paths.
Build Targets
Erlang Target
For Erlang target builds:
Uses gleam export erlang-shipment to create a standalone package
Creates a shell script wrapper that runs erl with the correct paths
Includes all BEAM bytecode and dependencies
Generated wrapper:
#!/usr/bin/env sh
/nix/store/.../erlang/bin/erl \
-pa $out /lib/ * /ebin \
-eval "myapp@@main:run(myapp)" \
-noshell \
-extra " $@ "
JavaScript Target
For JavaScript target builds:
Runs gleam build --target javascript
Creates an entry point module that imports and runs main()
Creates a shell script wrapper using Node.js
Generated wrapper:
#!/usr/bin/env sh
/nix/store/.../nodejs/bin/node $out /lib/myapp/main.mjs " $@ "
Elixir Support
If any dependency requires Elixir (has "mix" in build_tools), Elixir is automatically added to nativeBuildInputs.
Implementation Details
Dependency Installation
All dependencies are copied into build/packages/ during the configure phase:
mkdir -p build/packages
# Write packages.toml
cat << EOF > build/packages/packages.toml
[packages]
gleam_stdlib = "0.34.0"
gleam_json = "1.0.0"
EOF
# Copy each dependency with proper permissions
rsync --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r \
-r /nix/store/.../gleam_stdlib/ * build/packages/gleam_stdlib/
Local Package Path Rewriting
When using localPackages, the build function patches both manifest.toml and gleam.toml:
sed -i -e 's|"../shared"|"${./packages/shared}"|g' manifest.toml
sed -i -e 's|"../shared"|"${./packages/shared}"|g' gleam.toml
This ensures Gleam can find the packages in the Nix store.
Source Attribution
This implementation is adapted from arnarg/nix-gleam .
Source location: libs/gleam/build.nix:16
Complete Example
Project Structure
my-gleam-project/
├── gleam.toml
├── manifest.toml
├── src/
│ └── my_app.gleam
├── test/
│ └── my_app_test.gleam
└── flake.nix
gleam.toml
name = "my_app"
version = "1.0.0"
target = "erlang"
[ dependencies ]
gleam_stdlib = "~> 0.34"
gleam_erlang = "~> 0.25"
gleam_otp = "~> 0.10"
src/my_app.gleam
import gleam / io
pub fn main () {
io. println ( "Hello from Gleam!" )
}
flake.nix
{
inputs = {
nixpkgs . url = "github:nixos/nixpkgs/nixos-unstable" ;
nur . url = "github:your-org/nur-nix" ;
};
outputs = { self , nixpkgs , nur }:
nur . lib . mkFlake { } ( system :
let
pkgs = nixpkgs . legacyPackages . ${ system } ;
lib = nur . lib . ${ system } ;
in
{
packages . default = lib . gleam . build {
src = ./. ;
};
devShells . default = pkgs . mkShell {
packages = with pkgs ; [
gleam
erlang
rebar3
];
};
}
);
}
Building and Running
# Build
nix build
# Run
./result/bin/my_app
# Output: Hello from Gleam!
Best Practices
Commit manifest.toml : Ensures reproducible builds
Use target in gleam.toml : Makes intent clear
Leverage localPackages : Great for monorepos
Pin dependency versions : Use exact versions in gleam.toml for production
Rust Utilities Cross-compile Rust using cargo-zigbuild
Go Utilities Cross-compile Go applications