Development Guide

This guide outlines the development process for the Ethereum on ARM project, covering repository setup, dependency management, package creation, and image generation.

1. Clone the Repository

Begin by cloning the Ethereum on ARM repository from GitHub:

git clone https://github.com/EOA-Blockchain-Labs/ethereumonarm.git

2. Setup for Package Building (fpm-package-builder)

Navigate to the fpm-package-builder directory within the cloned repository:

cd ethereumonarm/fpm-package-builder

From here, you have two options for setting up the development environment: using Docker (recommended) or installing dependencies manually.

2.2 Install Dependencies Manually (Ubuntu 24.04)

If you prefer to install dependencies directly on an Ubuntu 24.04 system, follow these steps:

  • Prepare APT sources:

    echo "# See /etc/apt/sources.list.d/ for repository configuration" > /etc/apt/sources.list
    rm -f /etc/apt/sources.list.d/ubuntu.sources
    UBUNTU_CODENAME=$(lsb_release -cs)
    cat <<EOF > /etc/apt/sources.list.d/ubuntu-amd64.sources
    Types: deb
    URIs: http://us.archive.ubuntu.com/ubuntu
    Suites: ${UBUNTU_CODENAME} ${UBUNTU_CODENAME}-updates ${UBUNTU_CODENAME}-security ${UBUNTU_CODENAME}-backports
    Components: main restricted universe multiverse
    Architectures: amd64
    Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
    EOF
    cat <<EOF > /etc/apt/sources.list.d/ubuntu-arm64.sources
    Types: deb
    URIs: http://ports.ubuntu.com
    Suites: ${UBUNTU_CODENAME} ${UBUNTU_CODENAME}-updates ${UBUNTU_CODENAME}-security ${UBUNTU_CODENAME}-backports
    Components: main restricted universe multiverse
    Architectures: arm64
    Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
    EOF
    
  • Add ARM64 architecture and update package lists:

    dpkg --add-architecture arm64
    echo 'Acquire::http::Pipeline-Depth "0";' > /etc/apt/apt.conf.d/90localsettings
    apt-get update -y
    
  • Install development tools and dependencies:

    apt-get install -y libssl-dev:arm64 pkg-config software-properties-common docker.io docker-compose clang file make cmake gcc-aarch64-linux-gnu g++-aarch64-linux-gnu ruby ruby-dev rubygems build-essential rpm vim git jq curl wget python3-pip
    
  • Install FPM (fpm-package management tool for Ruby):

    gem install --no-document fpm
    
  • Add Go PPA and install Go:

    add-apt-repository -y ppa:longsleep/golang-backports
    apt-get update -y
    apt-get -y install golang-go
    
  • Install Rustup and add aarch64 target:

    curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable
    source ~/.cargo/env && rustup target add aarch64-unknown-linux-gnu
    
  • Configure linker for aarch64 Rust target:

    mkdir -p ~/.cargo && cat <<EOF > ~/.cargo/config
    [target.aarch64-unknown-linux-gnu]
    linker = "aarch64-linux-gnu-gcc"
    EOF
    
  • Add Node.js and Yarn installation:

    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
    export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && nvm install 20
    export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && npm install -g yarn
    

2.4. Create .deb Packages

Once your environment is set up (either manually or with Docker), you can create .deb packages.

  • To create all .deb packages, simply type make:

    make
    
  • New: To create a specific package directly from the root (e.g., geth, prysm), type:

    make geth
    make prysm
    
  • Alternatively, you can still navigate to the desired client or package directory (e.g., geth) and type make:

    cd geth
    make
    

3. Deep Dive: Understanding the Packaging Process

This section provides a detailed look at how the packaging process works, why we use FPM, and the structure of the repository.

3.1. Why FPM?

We use FPM (Effing Package Management) because it simplifies the process of creating packages for multiple platforms (Debian/Ubuntu .deb, RedHat/Fedora .rpm, etc.) from a single source directory. It abstracts away much of the complexity associated with native packaging tools like dpkg-deb or rpmbuild, allowing us to focus on the content and configuration of the package.

3.2. The Generic Packaging Procedure

The packaging process is automated via Makefile scripts in each client’s directory. The general workflow is:

  1. Prepare: The make prepare target downloads the upstream binary (e.g., Geth, Teku) and verifies its integrity.

  2. Stage: Files are organized into a sources/ directory that mirrors the target system’s file structure (e.g., /usr/bin, /etc/ethereum).

  3. Build: FPM takes the sources/ directory and combines it with metadata (version, maintainer, dependencies) and extra files (systemd units) to generate the final .deb or .rpm package.

3.3. Repository Structure & Key Files

Understanding the file structure is key to customizing packages. Here is a breakdown using Teku as an example:

  • Makefile: The heart of the build process. It defines variables like PKG_NAME, TEKU_VERSION, and FPM_OPTS. It orchestrates the download, staging, and FPM commands.

  • sources/: This directory acts as a “fake root” for the package.

    • sources/usr/bin/: Contains the executable binaries.

    • sources/etc/ethereum/: Contains default configuration files.

      • Example: l1-clients/consensus-layer/teku/sources/etc/ethereum/teku-beacon.conf defines the default flags for the Teku beacon node.

  • extras/: Contains files that are not part of the main file tree but are used by the package manager, such as systemd service units.

    • Example: l1-clients/consensus-layer/teku/extras/teku-beacon.service is the systemd unit file that manages the Teku service. It defines how the service starts, restarts, and what user it runs as.

  • scripts/ (optional): Contains post-install or pre-remove scripts that run during package installation or removal (e.g., creating a user, setting permissions).

By modifying files in sources/ or extras/, you can customize the default configuration or service behavior of the resulting package.

4. Adding a New Package

This section guides you through adding a new software package (Client, Tool, or Service) to the repository using our standardized templates.

4.1. Directory Setup

Navigate to fpm-package-builder and choose the appropriate category:

  • Layer 1: l1-clients/consensus-layer/ or l1-clients/execution-layer/

  • Layer 2: l2-clients/

  • Infrastructure: infra/ or infra/dvt/

  • Tools/Web3: tools/ or web3/

Instead of creating directories manually, copy the standard template:

# Example: Adding a new tool called "my-new-tool"
cp -r build-scripts/templates infra/my-new-tool
cd infra/my-new-tool

The template creates the following structure for you:

  • Makefile: The build script.

  • extras/: Directory for service files and scripts.

  • sources/: Directory for configuration files.

4.2. Customize the Package

Follow the detailed guide included in the template or view it online.

Key Steps:

  1. Makefile: Update PKG_NAME, PKG_DESCRIPTION, and the prepare target to download your specific binary.

    Important

    You MUST verify the upstream binary using SHA256 or GPG.

  2. Service File: Rename extras/service.service to extras/<pkg-name>.service and update the ExecStart command.

  3. Configuration: Rename sources/etc/ethereum/config.conf to sources/etc/ethereum/<pkg-name>.conf and set your default flags.

4.3. Test the Build

Run make inside your directory. If successful, the .deb will appear in the relative packages/ directory.

4.4. Test the Build

Run make inside your directory. If successful, the .deb will appear in the relative packages/ directory.

5. Image Creation Tool

The image-creation-tool directory contains scripts to build custom Armbian images for various Single Board Computers (SBCs).

4.1. Setup

Navigate to the image-creation-tool/ubuntu directory:

cd ethereumonarm/image-creation-tool/ubuntu

4.2. Building Images

The Makefile in this directory automates the download, customization, and packaging of Armbian images. It uses a standalone script modify_image.sh to robustly detect partitions and inject the necessary files.

  • Build all images:

    To build images for all supported devices, run:

    make all
    
  • Build a specific image:

    To build an image for a specific device (e.g., rock5b), run:

    make build DEVICE=rock5b
    

    Supported Devices:

    • rpi5 (Raspberry Pi 5)

    • rock5b (Radxa Rock 5B)

    • orangepi5-plus (Orange Pi 5 Plus)

    • nanopct6 (NanoPC T6)

4.3. Clean Up

To remove all generated images and downloaded files, run:

make clean

For more information, the existing documentation includes a Getting Started Guide and a Operation Guide, which offer further details on Ethereum and client management.

4.4. Cloud Image Builder

The image-creation-tool/cloud directory contains tools to build custom ARM64 cloud images for Ethereum nodes using HashiCorp Packer.

The templates are optimized for Full Nodes, defaulting to high-performance ARM64 instances (4 vCPUs, 16GB RAM) and large storage (2TB).

Prerequisites:

  1. Packer: Install Packer on your local machine.

    • MacOS: brew install packer

    • Linux: Follow instructions at packer.io

  2. Cloud Credentials: You must have active credentials for the provider you want to build for.

Usage:

AWS Images

  1. Initialize:

    cd image-creation-tool/cloud
    packer init aws.pkr.hcl
    
  2. Build: The default build uses a t4g.xlarge instance (4 vCPU, 16GB RAM) and a 2TB disk.

    # Using environment variables for auth (Recommended)
    export AWS_ACCESS_KEY_ID="your_key"
    export AWS_SECRET_ACCESS_KEY="your_secret"
    packer build aws.pkr.hcl
    
    # OR passing variables explicitly
    packer build \
      -var "aws_access_key=your_key" \
      -var "aws_secret_key=your_secret" \
      aws.pkr.hcl
    

GCP Images

  1. Initialize:

    packer init gcp.pkr.hcl
    
  2. Build: Requires a Service Account JSON file or Application Default Credentials. Defaults to t2a-standard-4 instance and 2TB disk.

    packer build \
      -var "project_id=your-project-id" \
      -var "account_file=path/to/key.json" \
      gcp.pkr.hcl
    

Azure Images

  1. Initialize:

    packer init azure.pkr.hcl
    
  2. Build: Defaults to Standard_D4ps_v5 instance and 2TB disk.

    # Ensure variables like ARM_CLIENT_ID are set in your environment OR pass them manually
    packer build \
      -var "resource_group=my-images-rg" \
      -var "client_id=..." \
      -var "client_secret=..." \
      -var "subscription_id=..." \
      -var "tenant_id=..." \
      azure.pkr.hcl
    

Customization:

You can override the following variables at build time:

Provider

Variable

Default Value

Description

All

disk_size_gb

2048 (2TB)

Root disk size. Required for Full Node sync.

AWS

instance_type

t4g.xlarge

4 vCPU, 16GB RAM.

GCP

machine_type

t2a-standard-4

4 vCPU, 16GB RAM.

Azure

vm_size

Standard_D4ps_v5

4 vCPU, 16GB RAM.

Example:

packer build -var "disk_size_gb=100" aws.pkr.hcl

Example: Successful AWS AMI Creation

When you run packer build aws.pkr.hcl, a successful build output will look like this:

==> ethereum-on-arm-aws.amazon-ebs.ethereum-node: Prevalidating AMI Name: ethereum-on-arm-node-2026-01-07-2018
==> ethereum-on-arm-aws.amazon-ebs.ethereum-node: Found Image ID: ami-0071c8c431eea0edb
==> ethereum-on-arm-aws.amazon-ebs.ethereum-node: Launching a source AWS instance...
==> ethereum-on-arm-aws.amazon-ebs.ethereum-node: Provisioning with shell script: scripts/provision.sh
    ethereum-on-arm-aws.amazon-ebs.ethereum-node: Updating system...
    ethereum-on-arm-aws.amazon-ebs.ethereum-node: Installing Ethereum packages...
    ethereum-on-arm-aws.amazon-ebs.ethereum-node: Installing Monitoring packages...
==> ethereum-on-arm-aws.amazon-ebs.ethereum-node: Stopping the source instance...
==> ethereum-on-arm-aws.amazon-ebs.ethereum-node: Waiting for the instance to stop...
==> ethereum-on-arm-aws.amazon-ebs.ethereum-node: Creating AMI: ethereum-on-arm-node-2026-01-07-2018
==> ethereum-on-arm-aws.amazon-ebs.ethereum-node: AMI: ami-0925af779d95c2b3e
==> ethereum-on-arm-aws.amazon-ebs.ethereum-node: Terminating the source AWS instance...
==> Builds finished. The artifacts of successful builds are:
--> ethereum-on-arm-aws.amazon-ebs.ethereum-node: AMIs were created:
us-east-1: ami-0925af779d95c2b3e

Start Node with Terraform

Once your AMI is created (e.g., ami-0925af779d95c2b3e), you can use it in Terraform to launch a node:

resource "aws_instance" "ethereum_node" {
  ami           = "ami-0925af779d95c2b3e"
  instance_type = "t4g.xlarge" # Recommended: 4 vCPUs, 16GB RAM

  root_block_device {
    volume_size = 2048 # 2 TB disk
    volume_type = "gp3"
  }

  tags = {
    Name = "Ethereum Node"
  }
}

Monthly Cost Simulation

Note

These cost simulations are estimates generated by AI and may vary based on region, pricing changes, and specific configuration.

Based on the default configuration (4 vCPU, 16GB RAM, 2TB SSD) in the US region:

Provider

Instance

Storage

Approx. Monthly Cost

AWS

t4g.xlarge

2TB gp3

~$260

GCP

t2a-standard-4

2TB pd-balanced

~$310

Azure

Standard_D4ps_v5

2TB Managed SSD

~$250