
Enable Kernel CONFIG Options: What to Know First
Stop looking for CONFIG_HAVE_EBPF_JIT in menuconfig. You will never find a prompt to toggle it, because it is a silent architecture capability flag, not a knob you set. The kernel build selects it automatically when your CPU architecture ships a just-in-time (JIT) compiler backend for the BPF virtual machine. Its whole job is to let CONFIG_BPF_JIT turn on, so eBPF programs compile to native machine code instead of crawling through the in-kernel interpreter. If you came here from a checklist telling you to enable it by hand, that checklist is wrong, and the sections below explain what to check instead.
Last updated: 2026-07-21
What does this config symbol actually describe?
It describes a marker, not a feature you switch on. CONFIG_HAVE_EBPF_JIT is a kernel configuration option that says "this architecture has code to translate eBPF bytecode into real instructions." That is it. The flag itself compiles nothing and enables no behavior on its own.
Think of it as a dependency gate. When it is set to y, the config system is allowed to offer and enable the actual JIT. When it is absent, your architecture has no JIT backend, and every eBPF program runs interpreted no matter what else you set.
So the real question is never "how do I enable HAVE_EBPF_JIT." The real question is whether your architecture provides it and whether the JIT that depends on it is switched on. Grep your running config, read arch/*/Kconfig, and confirm at runtime. I will walk each of those.
How is it different from CONFIG_BPF_JIT and CONFIG_BPF_JIT_ALWAYS_ON?
These three get conflated constantly, and the confusion sends people editing the wrong line. Here is the split that matters.
| Symbol | What it means | Can you set it? |
|---|---|---|
| CONFIG_HAVE_EBPF_JIT | The architecture has a JIT backend for eBPF | No, selected by the arch |
| CONFIG_BPF_JIT | Build the JIT into the kernel | Yes, if HAVE_EBPF_JIT is present |
| CONFIG_BPF_JIT_ALWAYS_ON | Force JIT on, refuse the interpreter | Yes |
CONFIG_HAVE_EBPF_JIT is the capability. CONFIG_BPF_JIT is the actual JIT you can turn on or off. CONFIG_BPF_JIT_ALWAYS_ON goes further and rips out the interpreter entirely, which is a hardening move: it removes a known attack surface and guarantees no program silently falls back to interpreted execution.
One more distinction people trip on. Classic BPF (the old socket-filter bytecode) and extended BPF (eBPF, the modern thing behind tracing, XDP, and cgroup hooks) share the JIT plumbing on most architectures. The EBPF in the flag name points at the extended path. If a blog talks about "BPF JIT" without saying which, assume eBPF on any current kernel.
Which architectures select the flag
The mainstream ones: x86-64, arm64, and other well-supported 64-bit architectures. The way to know for certain is to read the source, not a compatibility table someone typed once and never updated.
Open arch/x86/Kconfig (or your architecture's directory) and search for the select line. You will see something like select HAVE_EBPF_JIT inside the config X86_64 block. That single line is why the flag shows up already set in your config with no prompt attached.
grep -r "select HAVE_EBPF_JIT" arch/
Run that in your kernel source tree and it lists every architecture that opts in. If your architecture is not in the output, there is no JIT backend, full stop. No config edit invents one. That is the answer to most "why won't the JIT enable on my board" questions: it is a 32-bit or niche architecture that never shipped the code.
Getting the kernel config from a running system

You already have a copy of the config the running kernel was built with. You just need to read it. There are two reliable places to look, and the method depends on how your distro shipped things.
If your kernel exposes it, the config lives in memory:
zcat /proc/config.gz | grep -i ebpf_jit
That requires CONFIG_IKCONFIG and CONFIG_IKCONFIG_PROC to have been set when the kernel was built. Many distros skip them. If /proc/config.gz is missing, fall back to the file on disk:
grep -i ebpf_jit /boot/config-$(uname -r)
uname -r prints the running kernel version, so $(uname -r) picks the matching config file without you typing it. You want to see CONFIG_HAVE_EBPF_JIT=y and, ideally, CONFIG_BPF_JIT=y. If HAVE_EBPF_JIT is y but BPF_JIT is unset or n, the capability exists and the JIT is just switched off. That is a fixable build choice, not a hardware limit.
For the broader task of reading linux kernel configuration options on a live box, zcat and grep against these two locations answer nearly everything. Keep the pattern loose (grep -i) so case and partial symbol names still match.
Reading and managing the kernel config file directly
Do not copy a stranger's .config and hope. Editing your kernel config by hand, with the help text open, beats pasting someone else's file, because their file matches their hardware and their kernel version, not yours. A symbol they set may not even exist in your tree.
You want control over your system but fear breaking boot or losing drivers, and that is why learning how to enable kernel CONFIG options matters. The clean workflow is to pick a source tree, choose a config, and build a working kernel without wasting time or risking stability. Start from a known-good .config, change one thing, and know why.
I focus on hands-on steps: using menu-driven configurators like make menuconfig, saving a config file, and compiling with make -j$(nproc). make menuconfig is the ncurses-based, pseudo-graphical menu tool, and it stays reliable over SSH where the GTK and Qt front ends do not. When you need to see a symbol's dependencies before you touch it, press / inside menuconfig to search. It shows the type, the menu path, and what selects it.
For a deeper walk through the menu tool itself, see my guide on configuring a kernel with make menuconfig. If you are writing your own module and need to expose options, using Kconfig for a new kernel module covers the syntax.
What Kconfig environment variables affect whether this flag gets selected?
The build environment decides your architecture, and your architecture decides HAVE_EBPF_JIT. So the variables that matter are the ones that pick the target.
ARCHsets the target architecture. Build withARCH=armon a 32-bit target and HAVE_EBPF_JIT may never get selected, even on hardware whose 64-bit mode would have it.CROSS_COMPILEnames the toolchain prefix for cross builds. A mismatched prefix is the classic way to silently build for the wrong architecture and lose the JIT.KCONFIG_CONFIGpoints the tools at an alternate config file instead of.config.KCONFIG_ALLCONFIGforces selected values from a mini-config duringallyesconfigand friends.
Here is what happens when people get burned: they set ARCH for one thing and CROSS_COMPILE for another. The tree configures for a different architecture than the one they compile for. HAVE_EBPF_JIT comes out unset, and they blame the JIT. Check that make -s ARCH=... menuconfig is reading the architecture you think it is before anything else.
Expect practical tips for translating configuration choices into a bootable system, from comparing current and default configs to avoiding mixed toolchain pitfalls. A mixed toolchain is the pitfall that eats an afternoon.
Confirming the JIT is actually running, not just configured
Configured and running are two different claims. A kernel built with CONFIG_BPF_JIT can still refuse to JIT at runtime, because a sysctl controls it. Check that first:
cat /proc/sys/net/core/bpf_jit_enable
A 0 means the JIT is compiled in but disabled at runtime. A 1 means it JITs programs. A 2 means it JITs and dumps the generated code to the kernel ring buffer for debugging, which you read with dmesg. To turn it on for the current boot:
sudo sysctl net.core.bpf_jit_enable=1
That tells you the switch position. To see a real program get JIT-compiled, load one and inspect it with bpftool:
bpftool prog show
Look at the jited field in the output. If the program shows as jited, the verifier accepted it and the JIT turned it into native code. If that field is absent, it is running interpreted. That is the difference between "the config looks right" and "my program is actually compiled," and only the runtime check settles it.
How BPF JIT verification ties back to this config
Every eBPF program passes the in-kernel verifier before it runs, JIT or not. The verifier walks every path to prove the program cannot crash the kernel, loop forever, or read memory it should not. Only after it passes does the JIT get a chance to compile the accepted bytecode into machine instructions.
So HAVE_EBPF_JIT never weakens that safety check. The JIT operates on already-verified bytecode. Program type matters here too: socket filters, XDP, tracing, and cgroup programs all go through the same verify-then-JIT pipeline via the bpf() syscall, and helper functions and kfuncs are resolved as part of that. The JIT just changes how fast the verified program runs.
If you want the authoritative reference on the syscall and its program types, read the bpf() system call manual page. It is the primary source, and it stays closer to the kernel than most tutorials.
Why did this config symbol move between kernel releases?
Because config symbols get renamed, split, and reparented across releases, and an old guide checked against a new kernel will mislead you. HAVE_EBPF_JIT itself has been stable, but the symbols around it have shifted as BPF grew from a packet filter into a general execution engine.
Don't wait to find out the hard way. Reuse a known-good .config, drop it into the new tree, and let the tools tell you what changed. Copy the old config in and run make listnewconfig to see every symbol the new tree introduced. After make oldconfig, run scripts/diffconfig .config.old .config to catch the subtle renames.
This habit is cheap and it saves boots. A release that renames a symbol can silently drop a driver or a BPF feature you depended on. Comparing old against new before you build is how you catch it at your desk instead of at a dead console.
What to do if the flag is missing or the JIT won't enable
Work down the list, do not guess. Each step tells you something the next one depends on.
- Grep the running config.
grep -i ebpf_jit /boot/config-$(uname -r). If HAVE_EBPF_JIT is absent, go to step 2. If it isybut BPF_JIT is not, skip to step 4. - Check your architecture in source.
grep -r "select HAVE_EBPF_JIT" arch/. Not listed? Your architecture has no JIT backend and no config fixes that. You are on a 32-bit or niche target. - Check your build environment. Confirm
ARCHandCROSS_COMPILEpoint at the same 64-bit target. A mixed toolchain silently strips the flag. - Turn on the runtime switch.
cat /proc/sys/net/core/bpf_jit_enable. If it reads0, set it to1. Some distros ship it off by default. - Prove it at runtime. Load a program and check
bpftool prog showfor thejitedfield.
The fix is almost never a hand-edit of the config symbol. Usually it is the wrong kernel build for your architecture, a runtime sysctl left off, or a JIT deliberately disabled for security posture. Understand which one, or it comes back.
Keeping the distribution kernel as a safety net while you test a custom build is not optional. If a new build boots badly, you pick the old entry from the boot menu and you are back in minutes. My step-by-step kernel update guide covers keeping that rollback path real instead of theoretical.
FAQ
Does turning on the JIT make my eBPF programs less safe?
No. The verifier runs first and proves the program is safe before the JIT ever sees it. The JIT compiles already-approved bytecode into native code, so it changes speed, not safety. If you want a stronger guarantee, CONFIG_BPF_JIT_ALWAYS_ON removes the interpreter fallback entirely.
Can I enable the JIT on a running kernel without rebuilding?
Only if CONFIG_BPF_JIT was already built into your kernel. If it was, flip net.core.bpf_jit_enable to 1 with sysctl and it takes effect immediately. If BPF_JIT was never compiled in, no sysctl helps and you need a new kernel build.
Why does /proc/config.gz not exist on my system?
Your kernel was built without CONFIG_IKCONFIG_PROC, which many distros omit to save a little space. Read the on-disk copy instead: grep -i ebpf_jit /boot/config-$(uname -r). That file is written at install time and holds the same values.
Is CONFIG_HAVE_EBPF_JIT the same on x86-64 and arm64?
Both architectures select it, so on both you will see the flag set with no prompt. What can differ is the runtime default of the JIT sysctl and whether the distro built CONFIG_BPF_JIT at all. Check the config file and the sysctl on each box rather than assuming they match.
How do I tell if a specific program is JIT-compiled versus interpreted?
Run bpftool prog show and read the tag on your program. The jited tag means the JIT compiled it to native code; its absence means the program is running through the interpreter. Setting bpf_jit_enable=2 also dumps the generated instructions to dmesg if you want to see the actual output.
