Enable WireGuard on NVIDIA Jetson
By Toshihito Kikuchi
In the earlier article, I mentioned Dynamo supports ARM64 devices. At that time, I tested Amazon EC2 T4g Instances and Raspberry Pi 5. As a third ARM64 device to test, I purchased NVIDIA Jetson Orin Nano.
After landing several patches, I was able to run Dynamo on Jetson. However, you need to do one extra step before installing Dynamo on your device: Build and install WireGuard on your end. Well, I could provide pre-built binaries, but I’d like to encourage people to build linux kernel by yourself. It’s fun. And Jetson as a Kinesis Node won’t be a mainstream scenario anyway.
I saw several forums discussing this topic too. Hopefully this article will help non-Kinesis users as well.
Devices needed
I purchased:
NVIDIA Jetson Orin Nano Super Developer Kit (Amazon)
Memory Card (I bought 64GB UHS-1 via Amazon. You pick any product.)
You also need a display supporting Display Port and a USB keyboard if you don’t have any.
OS setup
Here's the official Getting Started Guide provided by NVIDIA: https://developer.nvidia.com/embedded/learn/get-started-jetson-orin-nano-devkit
In my case, the firmware was already up-to-date (36.4.x), so I directly downloaded SD Card Image of JetPack 6.2.1, but yours may not be. Don’t skip to check your firmware version. Please note that the latest version JetPack 7.0 is for Thor, not for Jetson Orin Nano (see https://developer.nvidia.com/embedded/jetpack-archive).
You can use your favorite writer to write an image to SD Card. I used balena Etcher. Once it’s done, just insert your card and power on your Jetson. No drama here.
Build WireGuard module
Let's double check WireGuard is not included in your device. You might be lucky to have it somehow. If it exists, it would be in /lib/modules/$(uname -r)/kernel/drivers/net/wireguard
.
jetson@jetson:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 22.04.5 LTS
Release: 22.04
Codename: jammy
jetson@jetson:~$ uname -a
Linux jetson 5.15.148-tegra #1 SMP PREEMPT Thu Sep 18 15:08:33 PDT 2025 aarch64 aarch64 aarch64 GNU/Linux
jetson@jetson:~$ ls -l /lib/modules/$(uname -r)/kernel/drivers/net
total 220
drwxr-xr-x 4 root root 4096 Oct 7 23:14 can
drwxr-xr-x 3 root root 4096 Jun 17 23:26 dsa
-rw-r--r-- 1 root root 13265 Sep 18 15:40 dummy.ko
drwxr-xr-x 10 root root 4096 Jun 17 23:26 ethernet
drwxr-xr-x 2 root root 4096 Oct 7 23:14 ipa
-rw-r--r-- 1 root root 39633 Sep 18 15:40 macvlan.ko
-rw-r--r-- 1 root root 12177 Sep 18 15:40 macvtap.ko
drwxr-xr-x 2 root root 4096 Oct 7 23:14 mdio
-rw-r--r-- 1 root root 10321 Sep 18 15:40 mdio.ko
drwxr-xr-x 2 root root 4096 Oct 7 23:14 pcs
drwxr-xr-x 2 root root 4096 Oct 7 23:14 phy
-rw-r--r-- 1 root root 51305 Sep 18 15:40 tap.ko
drwxr-xr-x 2 root root 4096 Oct 7 23:14 usb
-rw-r--r-- 1 root root 46089 Sep 18 15:40 veth.ko
drwxr-xr-x 2 root root 4096 Oct 7 23:14 vxlan
drwxr-xr-x 6 root root 4096 Oct 7 23:14 wireless
Since Linux 5.6, WireGuard is a part of kernel code in the mainstream, so you can get wireguard module by building linux kernel. Fortunately, NVIDIA provides a guide on how to customize kernel.
The guide says we can download the kernel source with a script named “source_sync.sh”. A weird thing is it doesn’t tell us where it is. It’s not included in the SD Card Image, JetPack. The answer is here. It’s included in Driver Package. You can go to Jetson Linux page and download "Driver Package (BSP)”. As of now, the package file is Jetson_Linux_r36.4.4_aarch64.tbz2, which includes ./Linux_for_Tegra/source/source_sync.sh.
To run source_sync.sh, you need to specify “release-tag”, which the guide says is included in the release notes. But which release note should we check? AI tells me to do
jetson@jetson:/work$ head -n1 /etc/nv_tegra_release
# R36 (release), REVISION: 4.7, GCID: 42132812, BOARD: generic, EABI: aarch64, DATE: Thu Sep 18 22:54:44 UTC 2025
In the output above, the release is 36.4.7, but there is no corresponding release note. Given that the release note of 36.4.4 says its release tag is “jetson_36.4.4”, we can make a guess. The source repo is https://nv-tegra.nvidia.com/r/admin/repos/3rdparty/canonical/linux-jammy,tags, so you can check the list of tags there.
jetson@jetson:/work/Linux_for_Tegra/source$ ./source_sync.sh -k -t jetson_36.4.7
Downloading default kernel/kernel-jammy-src source...
Cloning into '/work/Linux_for_Tegra/source/kernel/kernel-jammy-src'...
remote: Enumerating objects: 8617818, done.
remote: Counting objects: 100% (8617818/8617818), done.
remote: Compressing objects: 100% (1256098/1256098), done.
Receiving objects: 100% (8617818/8617818), 1.89 GiB | 14.65 MiB/s, done.
remote: Total 8617818 (delta 7322045), reused 8605598 (delta 7309852), pack-reused 0 (from 0)
Resolving deltas: 100% (7322045/7322045), done.
Checking objects: 100% (33554432/33554432), done.
The default kernel/kernel-jammy-src source is downloaded in: /work/Linux_for_Tegra/source/kernel/kernel-jammy-src
Syncing up with tag jetson_36.4.7...
Updating files: 100% (74252/74252), done.
Switched to a new branch 'mybranch_2025-10-08-1759908244'
/work/Linux_for_Tegra/source/kernel/kernel-jammy-src source sync'ed to tag jetson_36.4.7 successfully!
When we build linux kernel, we usually generate .config to customize build options with menuconfig
or other tools. For Jetson Linux, however, the guide suggests to use make -C kernel
to build, where Makefile applies defconfig to build by default. Even if we run menuconfig
, Makefile overrides .config with defconfig.
As you can see, CONFIG_WIREGUARD
is not defined there.
jetson@jetson:/work/Linux_for_Tegra/source$ grep WIRE kernel/kernel-jammy-src/arch/arm64/configs/defconfig
CONFIG_SOUNDWIRE=m
CONFIG_SOUNDWIRE_QCOM=m
Let’s simply add CONFIG_WIREGUARD=m
at the end of the file and kick off build. Once build is done, wireguard.ko is generated.
jetson@jetson:/work/Linux_for_Tegra/source$ tail -5 kernel/kernel-jammy-src/arch/arm64/configs/defconfig
# CONFIG_FUNCTION_GRAPH_TRACER is not set
# CONFIG_DYNAMIC_FTRACE is not set
CONFIG_MEMTEST=y
CONFIG_WIREGUARD=m
# Install prereq packages
jetson@jetson:/work/Linux_for_Tegra/source$ sudo apt install -y build-essential bc flex bison libssl-dev zstd libncurses
-dev
# Build! (Makefile specifies -j option accordingly)
jetson@jetson:/work/Linux_for_Tegra/source$ make -C kernel
...
jetson@jetson:/work/Linux_for_Tegra/source$ find . -name wireguard.ko
./kernel/kernel-jammy-src/drivers/net/wireguard/wireguard.ko
Install WireGuard module
To install a single module, you simply copy a file under /lib/modules, generate dependencies, and load with modprobe
.
sudo mkdir /lib/modules/$(uname -r)/kernel/drivers/net/wireguard/
sudo cp kernel/kernel-jammy-src/drivers/net/wireguard/wireguard.ko \
/lib/modules/$(uname -r)/kernel/drivers/net/wireguard/
sudo depmod -a
sudo modprobe wireguard
Oops, the last modprobe
command failed with the following error.
jetson@jetson:/work/Linux_for_Tegra/source$ sudo modprobe wireguard
modprobe: ERROR: could not insert 'wireguard': Unknown symbol in module, or unknown parameter (see dmesg)
Don’t panic. You can see more details with dmesg
.
jetson@jetson:~$ sudo dmesg | tail
...
[10962.061383] wireguard: Unknown symbol chacha20poly1305_encrypt_sg_inplace (err -2)
[10962.061509] wireguard: Unknown symbol chacha20poly1305_encrypt (err -2)
[10962.061715] wireguard: Unknown symbol chacha20poly1305_decrypt_sg_inplace (err -2)
[10962.061798] wireguard: Unknown symbol xchacha20poly1305_encrypt (err -2)
[10962.061820] wireguard: Unknown symbol xchacha20poly1305_decrypt (err -2)
[10962.061913] wireguard: Unknown symbol chacha20poly1305_decrypt (err -2)
These are simple dependency errors. Searching code, you can easily find these symbols are implemented in libchacha20poly1305.ko, which is not installed in the kernel.
jetson@jetson:/work/Linux_for_Tegra/source/kernel/kernel-jammy-src/lib/crypto$ grep -nr chacha20poly1305_encrypt_sg_inplace
grep: libchacha20poly1305.o: binary file matches
chacha20poly1305-selftest.c:8928: ret = chacha20poly1305_encrypt_sg_inplace(sg_src,
chacha20poly1305-selftest.c:9043: if (!chacha20poly1305_encrypt_sg_inplace(sg_src,
grep: libchacha20poly1305.ko: binary file matches
grep: chacha20poly1305.o: binary file matches
chacha20poly1305.c:333:bool chacha20poly1305_encrypt_sg_inplace(struct scatterlist *src, size_t src_len,
chacha20poly1305.c:341:EXPORT_SYMBOL(chacha20poly1305_encrypt_sg_inplace);
jetson@jetson:/work/Linux_for_Tegra/source$ nm -g kernel/kernel-jammy-src/lib/crypto/libchacha20poly1305.ko | grep chacha20poly1305_encrypt_sg_inplace
0000000000000bd0 T chacha20poly1305_encrypt_sg_inplace
0000000037b34b92 A __crc_chacha20poly1305_encrypt_sg_inplace
jetson@jetson:/work/Linux_for_Tegra/source$ find /sys/module/$(uname -r) -name libchacha*
find: ‘/sys/module/5.15.148-tegra’: No such file or directory
Let’s install this module and try to load wireguard again.
sudo cp ./kernel/kernel-jammy-src/lib/crypto/libchacha20poly1305.ko \
/lib/modules/$(uname -r)/kernel/lib/crypto/
sudo depmod -a
sudo modprobe wireguard
You’ll get the same error from modprobe
, but it’s caused by different symbols.
[11355.067989] libchacha20poly1305: Unknown symbol poly1305_final_arch (err -2)
[11355.068039] libchacha20poly1305: Unknown symbol poly1305_init_arch (err -2)
[11355.068089] libchacha20poly1305: Unknown symbol poly1305_update_arch (err -2)
Repeat the same thing, searching code, identifying a missing module implementing the symbols. This time it’s poly1305-neon.ko. Neon is SIMD extension for ARM.
jetson@jetson:/work/Linux_for_Tegra/source/kernel/kernel-jammy-src/arch/arm64/crypto$ grep -nr poly1305_final_arch
grep: poly1305-neon.o: binary file matches
grep: poly1305-neon.ko: binary file matches
grep: poly1305-glue.o: binary file matches
poly1305-glue.c:170:void poly1305_final_arch(struct poly1305_desc_ctx *dctx, u8 *dst)
poly1305-glue.c:182:EXPORT_SYMBOL(poly1305_final_arch);
poly1305-glue.c:191: poly1305_final_arch(dctx, dst);
jetson@jetson:/work/Linux_for_Tegra/source$ nm -g kernel/kernel-jammy-src/arch/arm64/crypto/poly1305-neon.ko | grep poly1305_final_arch
00000000f39f5240 A __crc_poly1305_final_arch
0000000000000e00 T poly1305_final_arch
Let’s just copy and try to load wireguard.ko once again.
sudo cp ./kernel/kernel-jammy-src/arch/arm64/crypto/poly1305-neon.ko \
/lib/modules/$(uname -r)/kernel/arch/arm64/crypto/
sudo depmod -a
sudo modprobe wireguard
It should work this time, and you’ll see it’s loaded as below.
jetson@jetson:/work/Linux_for_Tegra/source$ lsmod | grep wire
wireguard 77824 0
libchacha20poly1305 16384 1 wireguard
ip6_udp_tunnel 20480 1 wireguard
udp_tunnel 24576 1 wireguard
libcurve25519_generic 36864 1 wireguard
ipv6 471040 94 bridge,wireguard
Verification
Dynamo uses wireguard through our gateway container. You can simulate this scenario by creating a minimum wireguard conf and running a container as follows. An endpoint can be random which doesn’t need to be valid for this verification purpose.
jetson@jetson:/work$ cat <<EOF > ${PWD}/wg-test.conf
[Interface]
PrivateKey = 6KvR4DZ1+uZom3S4VOBlbMtDpuZGGnbNEIFJfD4cjUg=
Address = 10.200.100.1/24
[Peer]
PublicKey = xFpe/+vmrUzQwtqpvVLB4AXygknLQo3f/qRR0AFXflQ=
AllowedIPs = 10.200.100.2/32
Endpoint = 1.2.3.4:51820
PersistentKeepalive = 25
EOF
jetson@jetson:/work$ docker run -d \
--cap-add=NET_ADMIN \
--device /dev/net/tun \
--sysctl net.ipv4.ip_forward=1 \
-v ${PWD}/wg-test.conf:/etc/wireguard/wg0.conf:ro \
kinesisorg/gateway-container
250d35ac39c902515501ef863cf0d4cb91a2b407b75b6feab2fd6a41dc686c91
jetson@jetson:/work$ docker exec -it $(docker ps -q -f ancestor=kinesisorg/gateway-container) wg show
interface: wg0
public key: i0JM5N5EVUDJ/08h5N4mt6bohSPrtXJZcNFqrsOULzA=
private key: (hidden)
listening port: 49594
peer: xFpe/+vmrUzQwtqpvVLB4AXygknLQo3f/qRR0AFXflQ=
endpoint: 1.2.3.4:51820
allowed ips: 10.200.100.2/32
transfer: 0 B received, 592 B sent
persistent keepalive: every 25 seconds
Conclusion
Congratulations! Your Jetson device is ready to run Dynamo and join the Kinesis Network as a Global Node. In short, you need to clone Jetson Linux Kernel from NVIDIA’s repo, build it, and copy wireguard.ko along with a couple of dependent (crypto-related) modules. Pretty straightforward.
You might be interested in installing the kernel itself and debugging it. That’s good ambition. Let’s discuss it as another topic!
Last updated
Was this helpful?