If you’ve been following this free embedded Linux course, in the previous lecture we set up an NFS root filesystem so you could edit files on your host and see the changes instantly on the target. But there’s still one manual step slowing you down: copying a new kernel image to an SD card every time you rebuild it. This lecture closes that gap. We’ll TFTP boot a Linux kernel straight from your development host into U-Boot’s memory, over the network, with zero SD card re-flashing. If you’re building a free linux kernel development course workflow around fast iteration, this is the single change that saves the most time per day.
free linux device drivers course
free embedded linux course
free embedded systems course
TFTP boot Linux kernel
U-Boot netboot
What You Will Learn
- How TFTP fits into an embedded Linux development workflow, and why it beats SD-card reflashing
- How to set up a TFTP server on your Linux host in minutes
- How to TFTP boot a Linux kernel and device tree from U-Boot, on both 32-bit and 64-bit ARM targets
- How to combine a TFTP-loaded kernel with the NFS root filesystem from the previous lecture
- How to read and fix the most common TFTP boot failure, the dreaded repeating “T” timeout
- Security and performance considerations before you ever ship a TFTP-enabled bootloader
Prerequisites
- A board running U-Boot with a working Ethernet connection to your development host
- The NFS root filesystem staging area built in the previous lecture of this free linux device drivers course
- A built kernel image and device tree blob for your target
- Basic comfort with Linux networking (static IPs, subnets)
Why TFTP Boot a Linux Kernel Instead of Flashing
Every time you change a driver, rebuild the kernel, and want to test it, flashing that image onto an SD card and reinserting it into the board is slow and it wears out the card over many cycles. When you TFTP boot a Linux kernel, U-Boot instead pulls the kernel and device tree directly from your host’s RAM over Ethernet into the board’s RAM, in a few seconds, every single time. Paired with an NFS root filesystem, you get a fully network-served development loop: rebuild the kernel, rebuild a module, reboot the board, and none of it ever touches removable storage.
This is the standard workflow used by kernel and driver engineers, and it’s exactly the kind of practice this free linux kernel development course focuses on: fast, repeatable iteration over ad-hoc manual steps.
Setting Up a TFTP Server on Your Host
Modern Debian-based distributions make this simple with tftpd-hpa. Install and configure it to serve a directory you control:
$ sudo apt install tftpd-hpa
$ sudo mkdir -p /srv/tftp
$ sudo nano /etc/default/tftpd-hpa
Set the serve directory and options inside that file:
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure"
Restart the service and confirm it’s active before moving on — a huge fraction of TFTP boot failures trace back to this one step being skipped:
$ sudo systemctl restart tftpd-hpa
$ sudo systemctl status tftpd-hpa
Copy your freshly built kernel image and device tree into the serve directory:
$ cp arch/arm64/boot/Image /srv/tftp/
$ cp arch/arm64/boot/dts/vendor/my-board.dtb /srv/tftp/
|
| Ethernet
v
Target Board (U-Boot)
1. dhcp / setenv ipaddr, serverip
2. tftpboot -> Image (into RAM)
3. tftpboot -> my-board.dtb (into RAM)
4. booti / bootz (kernel + dtb from RAM)
|
v
Linux kernel boots, mounts NFS root over the same link
Configuring U-Boot Network Variables
On the target, drop into the U-Boot prompt and either use DHCP or set static addressing. DHCP is the quicker path when your lab network already hands out leases:
=> dhcp
If you prefer fixed addressing, which is more predictable for a dedicated bench setup, set it manually:
=> setenv ipaddr 10.0.0.20
=> setenv serverip 10.0.0.10
=> saveenv
Calling saveenv here matters — without it, every one of these variables resets the next time the board power-cycles, and you’ll be retyping them constantly.
TFTP Boot: Loading the Kernel and Device Tree
With networking configured, pull the kernel image and device tree into RAM at addresses appropriate for your SoC’s memory map (check your board’s documentation — these examples use generic, non-conflicting offsets):
=> tftpboot 0x82000000 Image
=> tftpboot 0x83000000 my-board.dtb
Then point the kernel command line at the NFS root filesystem you built in the previous lecture, and boot:
=> setenv bootargs console=ttyS0,115200 root=/dev/nfs rw
nfsroot=10.0.0.10:/srv/nfsroot ip=10.0.0.20:10.0.0.10::255.255.255.0
=> booti 0x82000000 - 0x83000000
On a 32-bit ARM target using the older zImage format, swap booti for bootz, with a compressed image path instead:
=> tftpboot 0x82000000 zImage
=> tftpboot 0x83000000 my-board.dtb
=> bootz 0x82000000 - 0x83000000
Reading a Successful TFTP Boot Log
A clean TFTP boot log looks like this — note the transfer completes and hands control straight to the kernel:
=> tftpboot 0x82000000 Image
eth0: link up, 1000Mbps full-duplex
Using ethernet@ff0e0000 device
TFTP from server 10.0.0.10; our IP address is 10.0.0.20
Filename 'Image'.
Load address: 0x82000000
Loading: #################################################
2.1 MiB/s
done
Bytes transferred = 24805376 (17a7c00 hex)
Troubleshooting a Failed TFTP Boot
The most common failure when you TFTP boot a Linux kernel shows up as a repeating string of the letter T instead of a progress bar:
=> tftpboot 0x82000000 Image
Using ethernet@ff0e0000 device
TFTP from server 10.0.0.10; our IP address is 10.0.0.20
Filename 'Image'.
Load address: 0x82000000
Loading: T T T T T T
Each T is a retransmitted request timing out. Work through these in order:
- Wrong server IP. Double-check
serveripmatches your host’s actual address on the interface connected to the board, not a VPN or Wi-Fi interface. - TFTP daemon not running. Re-check with
systemctl status tftpd-hpa; restart it if it’s inactive or failed. - Firewall blocking UDP port 69. Most host firewalls block TFTP by default. On a UFW-managed host:
sudo ufw allow 69/udp. - File not present or wrong permissions in the TFTP serve directory — confirm the filename in the log matches exactly, including case.
- Board and host on different subnets with no route between them — for a lab bench, a direct switch or crossover link avoids this entirely.
Common Mistakes
| Mistake | Effect | Fix |
|---|---|---|
| Kernel and DTB loaded at overlapping addresses | Silent corruption, kernel fails to boot or panics early | Leave generous spacing between load addresses; check your SoC’s memory map |
Forgetting saveenv |
Network settings vanish on reboot | Always saveenv after setting ipaddr/serverip |
| Using zImage boot command with an Image file, or vice versa | U-Boot reports an invalid image format | Match bootz to zImage, booti to Image |
| Testing over a busy shared network | Intermittent, hard-to-reproduce timeouts | Use a dedicated link or switch for bench TFTP traffic |
Best Practices
- Wrap your
tftpbootandbootargssequence into a U-Boot boot script (boot.scr) once it’s stable, so a barerun netbootreproduces the exact sequence every time. - Keep your NFS root and TFTP server directories on the same host for a single-machine dev loop.
- Version your
Image/zImageand.dtbfilenames if you’re regularly switching between kernel branches, to avoid loading a stale build by accident.
Security Considerations
TFTP has no authentication and no encryption — anyone on the same network segment can request any file your server exposes. This is acceptable for an isolated bench or lab network, but a TFTP-enabled boot path must never be left reachable on a production network or shipped in a released product. Restrict the TFTP server to a dedicated development VLAN, and disable network boot in U-Boot entirely (or lock the boot menu) before deployment.
Performance Considerations
TFTP is noticeably slower than reading from local eMMC or NVMe, since every packet is acknowledged individually over UDP. For a development loop where you’re loading a kernel dozens of times a day, that few extra seconds still beats physically reflashing an SD card — but for anything beyond a bench setup, this is strictly a development-time tool, not a production boot mechanism.
Summary and Key Takeaways
- TFTP boot lets U-Boot pull a kernel and device tree over Ethernet, skipping SD card reflashing entirely.
- Combined with NFS root, it gives you a fully network-served embedded Linux development loop.
- A repeating “T” in the load progress almost always means server IP, daemon status, or firewall rules.
- Never leave a TFTP-enabled boot path reachable outside an isolated development network.
Mastering this workflow is one of the highest-leverage skills in this free linux kernel development course, because it directly shortens every single build-test cycle you’ll run for the rest of your embedded Linux work.
Frequently Asked Questions
What is TFTP boot in U-Boot?
It’s the process where U-Boot uses the Trivial File Transfer Protocol to fetch a kernel image and device tree from a server on the network directly into the board’s RAM, instead of reading them from local storage.
Why does my TFTP boot show a row of “T” characters?
Each T represents a timed-out retransmission. It almost always means the TFTP daemon isn’t running, the server IP is wrong, or a firewall is blocking UDP port 69.
Do I need DHCP to TFTP boot a Linux kernel?
No. You can use DHCP for convenience, or set ipaddr and serverip manually for a predictable static bench setup.
What’s the difference between bootz and booti?
bootz boots a compressed zImage, typically used on 32-bit ARM targets. booti boots the uncompressed Image format used on modern 64-bit ARM (arm64) kernels.
Is TFTP boot safe to leave enabled in production?
No. TFTP has no authentication or encryption. It should be restricted to an isolated lab network and disabled entirely before a product ships.
Can I combine TFTP boot with an NFS root filesystem?
Yes — this is the standard embedded Linux development pattern. The kernel and device tree load over TFTP, then the kernel mounts its root filesystem over NFS using the bootargs shown in this lecture.
Why should I use saveenv after setting ipaddr and serverip?
Without saveenv, U-Boot’s environment resets to defaults on the next power cycle, and you’ll have to retype your network settings every time you reboot the board.
Keep building your embedded Linux skills
This lecture is part of EmbeddedPathashala’s free embedded Linux course — follow along for the full free linux device drivers course and free linux kernel development course series.

2 Comments