Skip to content

Ubuntu Development Setup

This guide provides comprehensive instructions for setting up a Ghaf development environment on Ubuntu or Debian-based systems. Both automated and manual installation methods are covered.

Before you begin, ensure your system meets the following requirements:

RequirementMinimumRecommended
Ubuntu Version22.04 LTS24.04 LTS
Architecturex86_64x86_64
RAM8 GB16 GB or more
Disk Space50 GB free100 GB or more
NetworkStable internetFast broadband

Ubuntu 24.04 introduces stricter AppArmor policies that may affect Nix sandboxing. The automated setup script handles this automatically, but if you’re installing manually, see the AppArmor Configuration section.


The automated setup script handles all installation and configuration steps with interactive prompts for operations requiring elevated privileges.

  1. Clone the Ghaf repository:

    Terminal window
    git clone https://github.com/tiiuae/ghaf.git
    cd ghaf
  2. Run the setup script:

    Terminal window
    ./scripts/ubuntu-setup.sh
  3. Activate your shell configuration:

    Terminal window
    source ~/.bashrc # or ~/.zshrc for Zsh users
  4. Allow direnv for the Ghaf project:

    Terminal window
    direnv allow
OptionDescription
--acceptAccept all prompts automatically (non-interactive mode)
--uninstallRemove Nix and related configurations
--helpShow help message

Non-interactive installation (for CI/CD or automated setups):

Terminal window
./scripts/ubuntu-setup.sh --accept

Uninstall (removes Nix and configurations):

Terminal window
./scripts/ubuntu-setup.sh --uninstall

The automated script performs the following steps:

  1. System Dependencies: Installs curl, git, and xz-utils via apt
  2. Nix Installation: Installs Nix using the Determinate Systems installer
  3. Binary Cache: Configures the Ghaf binary cache for faster builds
  4. direnv Setup: Installs and configures direnv with nix-direnv
  5. Shell Integration: Adds direnv hook to your shell configuration
  6. AppArmor (Ubuntu 24.04): Configures user namespaces for Nix sandboxing
  7. binfmt (Optional): Sets up QEMU for AArch64 emulation
  8. VSCode (Optional): Creates VSCode configuration files

If you prefer to install components manually or need more control over the setup process, follow these steps.

Install the required system packages:

Terminal window
sudo apt update
sudo apt install -y curl git xz-utils

We recommend the Determinate Systems Nix Installer for Ubuntu systems:

Terminal window
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install

Benefits:

  • ✅ Flakes enabled by default
  • ✅ Clean uninstall support (/nix/nix-installer uninstall)
  • ✅ Multi-user mode (more secure)
  • ✅ Better enterprise support

After installation, restart your terminal or source the Nix profile:

Terminal window
# For Determinate Systems installer
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
# Or restart your terminal

Verify the installation:

Terminal window
nix --version

Configure the Ghaf binary cache for significantly faster builds. Create or edit ~/.config/nix/nix.conf:

Terminal window
mkdir -p ~/.config/nix
cat >> ~/.config/nix/nix.conf << 'EOF'
# Ghaf development configuration
experimental-features = nix-command flakes
# Binary caches for faster builds
substituters = https://ghaf-dev.cachix.org https://cache.nixos.org
trusted-public-keys = ghaf-dev.cachix.org-1:S3M8x3no8LFQPBfHw1jl6nmP8A7cVWKntoMKN3IsEQY= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
# Trust the Ghaf binary cache
extra-trusted-substituters = https://ghaf-dev.cachix.org
# Recommended settings
keep-outputs = true
keep-derivations = true
EOF

Restart the Nix daemon to apply changes:

Terminal window
sudo systemctl restart nix-daemon

direnv automatically loads the Ghaf development environment when you enter the project directory.

Terminal window
nix profile install nixpkgs#direnv
nix profile install nixpkgs#nix-direnv

Create the direnv configuration file:

Terminal window
mkdir -p ~/.config/direnv
cat > ~/.config/direnv/direnvrc << 'EOF'
# Use nix-direnv for better caching and performance
if [ -f "${HOME}/.nix-profile/share/nix-direnv/direnvrc" ]; then
source "${HOME}/.nix-profile/share/nix-direnv/direnvrc"
elif [ -f "/nix/var/nix/profiles/default/share/nix-direnv/direnvrc" ]; then
source "/nix/var/nix/profiles/default/share/nix-direnv/direnvrc"
fi
# Increase timeout for Nix operations
export DIRENV_WARN_TIMEOUT=60s
EOF

Add the direnv hook to your shell configuration:

Terminal window
echo 'eval "$(direnv hook bash)"' >> ~/.bashrc
source ~/.bashrc
Terminal window
git clone https://github.com/tiiuae/ghaf.git
cd ghaf
direnv allow

The development environment will now load automatically when you enter the directory.


Ubuntu 24.04 has stricter AppArmor policies that may affect Nix sandboxing. If you encounter sandbox-related build errors, configure user namespaces:

Terminal window
# Create sysctl configuration
echo 'kernel.unprivileged_userns_clone=1' | sudo tee /etc/sysctl.d/99-nix-userns.conf
# Apply immediately
sudo sysctl --system

For building AArch64 targets via emulation (alternative to cross-compilation):

Terminal window
sudo apt install -y qemu-user-static binfmt-support
sudo update-binfmts --enable qemu-aarch64

Remote builders allow offloading Nix builds to more powerful machines. This is useful for:

  • Building AArch64 targets on native ARM64 hardware (faster than cross-compilation)
  • Speeding up builds using dedicated build servers
  • Distributed builds across multiple machines

The automated setup script can configure remote builders interactively. For manual configuration:

  1. SSH access to the remote build machine
  2. Nix installed on the remote machine
  3. Remote machine configured to accept builds (see Remote Build Setup)
  1. Create the machines file (/etc/nix/machines):
Terminal window
sudo mkdir -p /etc/nix
sudo tee /etc/nix/machines << 'EOF'
# Format: ssh://user@host system ssh-key max-jobs speed-factor features mandatory-features public-key
ssh://builder@build-server.example.com x86_64-linux /home/user/.ssh/id_ed25519 8 1 nixos-test,benchmark,big-parallel,kvm - -
ssh://builder@arm-server.example.com aarch64-linux /home/user/.ssh/id_ed25519 16 1 nixos-test,benchmark,big-parallel,kvm - -
EOF
  1. Configure nix.conf to use remote builders:
Terminal window
cat >> ~/.config/nix/nix.conf << 'EOF'
# Remote build machines configuration
builders = @/etc/nix/machines
builders-use-substitutes = true
EOF
  1. Add the remote host to known_hosts:
Terminal window
# Fetch and add the host key
ssh-keyscan -t ed25519 build-server.example.com | sudo tee -a /etc/ssh/ssh_known_hosts
  1. Restart the Nix daemon:
Terminal window
sudo systemctl restart nix-daemon
  1. Test the connection:
Terminal window
nix store ping --store ssh://builder@build-server.example.com

Each line in /etc/nix/machines specifies a build machine:

ssh://USER@HOST SYSTEM SSH_KEY MAX_JOBS SPEED_FACTOR FEATURES MANDATORY_FEATURES PUBLIC_KEY
FieldDescriptionExample
USER@HOSTSSH connectionbuilder@server.com
SYSTEMTarget system(s)x86_64-linux or aarch64-linux
SSH_KEYPath to private key/home/user/.ssh/id_ed25519
MAX_JOBSParallel build jobs8
SPEED_FACTORPriority (higher = preferred)1
FEATURESSupported featuresnixos-test,benchmark,big-parallel,kvm
MANDATORY_FEATURESRequired features- (none)
PUBLIC_KEYBase64 host key- (use known_hosts)

For the best development experience with VSCode, install the recommended extensions and configure user settings.

Install the following VSCode extensions:

  • Nix IDE (jnoortheen.nix-ide) - Nix language support
  • direnv (mkhl.direnv) - Environment integration
  • EditorConfig (editorconfig.editorconfig) - Code style
  • ShellCheck (timonwong.shellcheck) - Shell script linting

Install via command line:

Terminal window
code --install-extension jnoortheen.nix-ide
code --install-extension mkhl.direnv
code --install-extension editorconfig.editorconfig
code --install-extension timonwong.shellcheck

For VSCodium users, replace code with codium.

Add Nix-specific settings to your VSCode user configuration. The settings file is located at:

  • VSCode: ~/.config/Code/User/settings.json
  • VSCodium: ~/.config/VSCodium/User/settings.json

If the file exists, merge the following settings. If it doesn’t exist, create it:

{
"nix.enableLanguageServer": true,
"nix.serverPath": "nixd",
"nix.serverSettings": {
"nixd": {
"formatting": {
"command": ["nixfmt"]
}
}
},
"[nix]": {
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.formatOnSave": true
},
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true
}

With the direnv extension installed, VSCode will automatically:

  1. Detect the .envrc file in the Ghaf project directory
  2. Load the Nix development environment
  3. Make tools like nixd, nixfmt, treefmt available to extensions

You’ll see a notification asking to allow direnv when opening the Ghaf project for the first time.


After installation, verify that everything is working correctly:

Terminal window
# Check Nix version
nix --version
# Check flakes support
nix flake --help
Terminal window
# Check direnv version
direnv version
# In the Ghaf directory, check that environment loads
cd ghaf
direnv status
Terminal window
cd ghaf
direnv allow
# Quick test: build documentation (5-10 minutes)
nix build .#doc
# Full test: build VM (45-90 minutes with binary cache)
nix build .#packages.x86_64-linux.vm-debug

Verify that the binary cache is being used:

Terminal window
# During a build, you should see downloads from ghaf-dev.cachix.org
nix build .#doc --print-build-logs 2>&1 | head -20

”experimental feature ‘flakes’ is disabled”

Section titled “”experimental feature ‘flakes’ is disabled””

Your Nix configuration doesn’t have flakes enabled. Add to ~/.config/nix/nix.conf:

experimental-features = nix-command flakes

Then restart the Nix daemon:

Terminal window
sudo systemctl restart nix-daemon

“error: could not set permissions on ‘/nix/var/nix/profiles/per-user’”

Section titled ““error: could not set permissions on ‘/nix/var/nix/profiles/per-user’””

This indicates a single-user Nix installation issue. Reinstall Nix in multi-user mode:

Terminal window
# Uninstall (if using Determinate Systems)
/nix/nix-installer uninstall
# Reinstall
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install

If you see errors related to sandboxing or user namespaces:

Terminal window
# Enable unprivileged user namespaces
echo 'kernel.unprivileged_userns_clone=1' | sudo tee /etc/sysctl.d/99-nix-userns.conf
sudo sysctl --system
  1. Ensure direnv hook is in your shell configuration
  2. Run direnv allow in the Ghaf directory
  3. Check direnv status: direnv status

If builds are taking longer than expected:

  1. Verify binary cache is configured:

    Terminal window
    grep "ghaf-dev.cachix.org" ~/.config/nix/nix.conf
  2. Check network connectivity to the cache:

    Terminal window
    curl -I https://ghaf-dev.cachix.org
  3. Explicitly use the cache:

    Terminal window
    nix build .#target --option substituters "https://ghaf-dev.cachix.org https://cache.nixos.org"

If you encounter issues not covered here:

  1. Check the Ghaf GitHub Issues
  2. Review the Nix manual
  3. Ask in the NixOS Discourse

For isolated or reproducible development environments, you can use the provided helper scripts to create Docker containers or QEMU virtual machines pre-configured for Ghaf development.

The dev-container.sh script creates a Docker container with all Ghaf development tools installed.

Requirements:

  • Docker installed (sudo apt install docker.io)
  • User in docker group (sudo usermod -aG docker $USER)

Quick Start:

Terminal window
# Create a container with current directory mounted
./scripts/dev-container.sh -m $(pwd) --accept create
# SSH into the container
./scripts/dev-container.sh ssh
# Or open a shell directly
./scripts/dev-container.sh shell

Options:

OptionDefaultDescription
-n, --nameghaf-devContainer name
-m, --mount(auto)Directory to mount in container
-c, --cpus4Number of CPUs
-r, --ram8gMemory limit
-p, --ssh-port2222SSH port on host
-P, --passwordghafSSH password
--accept-Auto-accept all prompts

Commands: create, start, stop, shell, ssh, remove, status

The dev-vm.sh script creates a full Ubuntu VM for Ghaf development, useful when you need complete isolation or to test the setup process itself.

Requirements:

  • QEMU and KVM (sudo apt install qemu-system-x86 qemu-utils)
  • cloud-image-utils (sudo apt install cloud-image-utils)
  • KVM support (check with ls /dev/kvm)

Quick Start:

Terminal window
# Create a VM with 120GB disk (recommended for Jetson builds)
./scripts/dev-vm.sh -d 120 -r 16 --accept create
# SSH into the VM
./scripts/dev-vm.sh ssh
# Stop the VM
./scripts/dev-vm.sh stop

Options:

OptionDefaultDescription
-n, --nameghaf-dev-vmVM name
-m, --mount-Directory to mount via 9p
-c, --cpus4Number of CPUs
-r, --ram16Memory in GB
-d, --disk120Disk size in GB
-p, --ssh-port2223SSH port on host
-P, --passwordghafSSH password
-D, --vm-dir~/ghaf-vmsVM files directory
--accept-Auto-accept all prompts

Commands: create, start, stop, ssh, remove, status


Terminal window
./scripts/ubuntu-setup.sh --uninstall

If you installed Nix with the Determinate Systems installer:

Terminal window
/nix/nix-installer uninstall

Remove configuration files:

Terminal window
rm -rf ~/.config/nix
rm -rf ~/.config/direnv

Remove direnv hook from your shell configuration (~/.bashrc or ~/.zshrc).


After setting up your development environment:

  1. Build and Run Guide - Learn how to build Ghaf images
  2. Cross Compilation - Build ARM targets from x86_64
  3. Development Guide - Contributing to Ghaf
  4. Remote Build Setup - Set up distributed builds