Library / SDK
aya-rs/aya avatar
aya-rs/aya

aya: writing eBPF programs in Rust without libbpf or a C toolchain

Aya is an eBPF library for the Rust programming language, built with a focus on developer experience and operability.

4,853 stars480 forksRustApache-2.0

At a glance

What is it?
A Rust eBPF library that talks to the kernel directly through syscalls, compiles without kernel headers, and supports BTF so one binary runs across kernel versions.
Who is it for?
aya is the right choice for a team already writing Rust that needs eBPF, because it removes the C toolchain, the kernel headers and the libbpf dependency from the build, and because async support exists for both tokio and async-std rather than being bolted on later. It is the wrong choice if you want a large body of existing C eBPF code, or if you need a kernel feature the library does not yet wrap.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What it means to build eBPF from the ground up in Rust

eBPF is the Linux mechanism for running user-supplied programs inside the kernel, and the traditional way to write one has been C. Aya takes a different route. The README is specific that it does not rely on libbpf nor bcc, and that it is built from the ground up purely in Rust using only the `libc` crate to execute syscalls. That means no vendored C library to build, no libbpf headers to track, and no dependency on the BCC toolchain being installed on the machine that compiles your code.

The second claim on the list is about build time and prerequisites: aya does not require a kernel build or compiled headers, and not even a C toolchain, and a release build completes in a matter of seconds. For a CI pipeline that builds for several architectures, this is not a minor convenience. The conventional eBPF build story involves matching a kernel tree to your distribution, and that mismatch is a recurring source of broken builds.

The repository topics are bpf, ebpf, observability, rust and security, which is a fair summary of where this library gets used: networking and security tooling where the kernel is the only place that can see the event.

BTF and compile once run everywhere

The portability feature is BTF, the BPF Type Format. The README describes it as transparently enabled when supported by the target kernel, and the effect is that an eBPF program compiled against one kernel version can run on different kernel versions without recompiling.

That is a real change in how you deploy this software. Without type information, an eBPF object is tied to the kernel it was built against, which in practice means a rebuild and a per-distribution artifact. With BTF, you produce one binary and it runs on the range of kernels that support it. The README links the original BPF portability write-up as the background, which is the right place to go if you want the mechanism rather than the summary.

Two smaller capabilities are listed alongside it: support for function call relocation and global data maps, which together allow eBPF programs to make function calls and to use global variables and initializers. Neither sounds remarkable in isolation, but both are the difference between a program that fits in a few hundred lines and one that needs to be broken into pieces to stay within verifier limits.

Loading and attaching a program, as the README shows it

The example in the README is short enough to read in one pass and shows the full lifecycle. You load a compiled object from disk, pull a typed program out of it by name, load it into the kernel, then attach it to a hook:

rust
use std::fs::File;
use aya::Ebpf;
use aya::programs::{CgroupSkb, CgroupSkbAttachType, CgroupAttachMode};

// load the BPF code
let mut ebpf = Ebpf::load_file("ebpf.o")?;

// get the `ingress_filter` program compiled into `ebpf.o`.
let ingress: &mut CgroupSkb = ebpf.program_mut("ingress_filter")?.try_into()?;

// load the program into the kernel
ingress.load()?;

The type tells you the hook. This is a `BPF_PROG_TYPE_CGROUP_SKB` program, so it attaches to the cgroup socket buffer rather than to a tracepoint or an XDP program, and the attachment opens `/sys/fs/cgroup/unified` and calls `attach` with `CgroupSkbAttachType::Ingress` and `CgroupAttachMode::AllowOverride`. The comment explains the consequence: `ingress_filter` will be called for all incoming packets on that cgroup.

The shape of the API is the interesting part. `Ebpf::load_file` gives you an object, `program_mut` with a `try_into` gives you a typed handle, and the typed handle is what knows how to attach. You never write a program type as a raw integer, which is the usual complaint about writing eBPF in C.

The workspace is a family of crates, not one library

The tree is long, and the Cargo workspace members section explains why. The workspace includes `aya` itself, `aya-build` for build-time logic, `aya-log` plus `aya-log-common` and `aya-log-parser` for the logging path, `aya-obj` for the object format, `aya-tool` for the command line companion, `ebpf-panic`, `xtask` for repository tasks, the macro crates `aya-ebpf-macros` and `aya-log-ebpf-macros`, and a set of eBPF-side crates under `ebpf/` including `aya-ebpf`, `aya-ebpf-bindings` and `aya-ebpf-cty`.

Note that the eBPF-side crates are deliberately excluded from `default-members`, and the workspace file explains why with a comment: they must be built with an explicit `RUSTFLAGS` setting for the target architecture and a `bpfe`-prefixed target such as `bpfe` or `bpfl` unknown-none. A plain `cargo build` will not build them. That is not a papercut so much as a statement that there are two compilation environments, one for the host and one for the kernel, and the workspace is careful not to conflate them.

The workspace also pins the toolchain expectations directly: edition 2024 and `rust-version = "1.98.0"`. Rust 1.98 is not an old release, so that floor will exclude some CI images you may be using today.

Async, build tooling and what the releases show

Async support is listed as working with both tokio and async-std. For an eBPF tool, that matters in a specific way: the program runs in the kernel, but the loading, attaching and event consumption happen in userspace, and an event loop is exactly what you need to read from a perf or ring buffer. Being runtime-agnostic means the library does not force that choice on you.

The build infrastructure has moved to Bazel. The tree contains `BUILD.bazel`, `MODULE.bazel`, `.bazelrc`, `.bazelversion` and a `bazel/` directory, and the aya-ebpf-v0.2.1 release notes list switching all builds and tests to Bazel as one of its changes. That release, published on 2026-06-30, also keeps verifier-safe intrinsics in aya-ebpf and fixes typos. Two days earlier, aya-v0.14.0 shipped, and aya-obj-v0.3.0 the same day, whose notes include adding a `bpf_strncmp` helper and letting QEMU fall back.

That QEMU detail is worth noticing. Kernel eBPF testing needs a kernel to run in, and QEMU is how you get one without root and without a nested container. Building the test story on QEMU is how a project like this can test against kernel revisions in CI rather than only against whatever kernel the runner happens to have. The repository is not archived and the last push was on 2026-09-22.

Licensing is dual, MIT or Apache 2.0 at your option, which the README states explicitly, with contributions dual licensed under the same terms unless stated otherwise.

Where aya is the wrong tool

The clearest case against is an existing C codebase of eBPF programs. Rewriting working BPF C in Rust to use aya is a large, low-value migration for a team that already has the toolchain set up. The library's value is concentrated in the build story, and a team that already solved the build story gets less from it.

The second case is feature coverage. The README says aya supports a large chunk of the eBPF API, which is an honest way of saying not all of it. The verifier is unforgiving, and a program type or helper that aya does not yet wrap is a wall. The open issue count on the repository is over two hundred, which for a project at this stage is more a measure of the size of the kernel surface than a verdict on quality, but it does mean the gap list is not short.

Third, this is Linux only. eBPF does not exist elsewhere, so there is no portability story to the question at all. And there is a legitimate alternative for Rust specifically: libbpf-rs binds libbpf from Rust, which buys you the maturity of the C library and the enormous existing body of BPF tooling at the cost of the C dependency aya exists to remove. Which side of that trade you fall on depends entirely on whether your build environment can carry a C toolchain.

Editorial conclusion

aya is the right choice for a team already writing Rust that needs eBPF, because it removes the C toolchain, the kernel headers and the libbpf dependency from the build, and because async support exists for both tokio and async-std rather than being bolted on later. It is the wrong choice if you want a large body of existing C eBPF code, or if you need a kernel feature the library does not yet wrap. The practical starting point is the book at aya-rs.dev/book, because the README here is a page long and deliberately defers everything else to it, and the second thing is the workspace layout in Cargo.toml, which explains why aya is a family of crates rather than one. Releases aya-v0.14.0 and aya-ebpf-v0.2.1 both landed in June 2026, with the last push on 2026-09-22.

Frequently asked questions

What is aya and what is an eBPF project?

eBPF is a Linux technology that runs user-supplied programs inside the kernel. Aya is a Rust library for writing those programs, built without libbpf or bcc and using only the libc crate for syscalls, so you do not need kernel headers or a C toolchain to build it.

How does BTF help with deploying eBPF programs?

BTF carries type information with the compiled object, and aya enables it transparently when the target kernel supports it. That lets a program compiled against one kernel version run on other kernel versions without recompiling, so one binary covers many distributions.

Should I use aya or libbpf-rs for eBPF in Rust?

Aya removes the C toolchain and kernel headers from the build and compiles in seconds, while libbpf-rs wraps the mature C library and its existing tooling. Choose aya when build portability across kernels and distributions matters most, and libbpf-rs when you want the breadth of the libbpf ecosystem already in place.

Official sources

  1. aya-rs/aya on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/aya-rs-aya.svg)](https://hysenlabs.com/projects/aya-rs-aya)