eBPF is a technology allowing you to dynamically program the kernel using a virtual machine approach. Several development frameworks exist, with the top choice for Rust developers being Aya.

Because of the deep integration between eBPF and the Linux kernel, it can be challenging to create a local development environment that works across different development machines. To date, I’ve taken to developing eBPF programs on EC2 instances using the AWS Cloud9 development environment. This works, but comes with its own issues and won’t be for everyone.

An alternative I’ve been exploring lately is using devcontainers to create a reproducible Docker container for eBPF development. Although devcontainers are best supported by Visual Studio Code, the hope is that this standard becomes more widely used and applicable to other environments.

Dockerfile

The Dockerfile I’ve built for this purpose is based on the devcontainer image available from Microsoft. From this starting point, we install llvm alongside required dependencies. This is done using the llvm.sh script. Note that we need to also install the development packages for libclang, libpolly, and libzstd. Rust is installed using the nightly toolchain as prescribed by the Aya documentation. Lastly, we install Aya’s BPF tools.

Putting this all together gives the following Dockerfile:

FROM mcr.microsoft.com/devcontainers/rust:1-bullseye

# Install llvm
RUN apt-get update \
  && apt install -y lsb-release wget software-properties-common gnupg \
  && wget https://apt.llvm.org/llvm.sh \
  && chmod a+x llvm.sh \
  && ./llvm.sh 18 \
  && apt install -y libclang-18-dev libpolly-18-dev libzstd-dev

# Install Rust with nightly sources
ENV RUST_VERSION=stable
USER vscode
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=$RUST_VERSION
RUN rustup toolchain install nightly --component rust-src
RUN export LLVM_SYS_160_PREFIX=/usr/include/llvm-18
ENV PATH "/usr/include/llvm-18/bin:$PATH:/usr/local/cargo/bin"

# BPF and aya tools
RUN cargo install --no-default-features bpf-linker
RUN cargo install bindgen-cli
RUN cargo install --git https://github.com/aya-rs/aya -- aya-tool

devcontainer.json

With our Dockerfile in place, the only thing we need is a devcontainer.json file that references the Dockerfile. The relevant sections of this JSON file are to run the container as a privileged user so we can add eBPF programs to the kernel using "privileged": true, add the BPF capability to the container using --cap-add=CAP_BFP, and mount the debug filesystem with sudo mount -t debugfs debugfs /sys/kernel/debug/ as a post create command.

Together, we get the following JSON file.

{
  "name": "epbf-devcontainer",
  "build": {
    "dockerfile": "Dockerfile"
  },
  "privileged": true,
  "runArgs": [
    "--cap-add=CAP_BPF"
  ],
  "postCreateCommand": "sudo mount -t debugfs debugfs /sys/kernel/debug/"
}

Place these two files in a .devcontainer/ folder and enjoy writing eBPF programs in a portable developmet environment!