HomeTopicsTFTP › Running a Server

Running a TFTP Server Properly

On paper, running a TFTP server is the easiest job in networking: install one package, point it at one folder, done. That apparent triviality is exactly why so many TFTP servers are quietly broken — nobody believed anything this simple needed attention, so nobody gave it any. Then a switch upgrade fails at midnight with %Error opening tftp://... (Timed out) and the "trivial" server gets an hour of frantic attention it never got at setup time.

TFTP servers also fail in an unusually unhelpful way. The clients are often boot ROMs and recovery loaders — software too small to explain itself, capable of reporting little more than "timeout" or "file not found." All the diagnostic burden lands on the server side. A server set up properly, with logging on and a few habits in place, turns those mysteries into two-minute reads of a log file.

This article is the server side done right: choosing the machine's role, a clean installation you can copy, root directory and permission discipline, the tuning knobs that matter, and the troubleshooting playbook for the classic failures. It is part of our TFTP series — the protocol behavior underneath (lock-step blocks, the port-69-then-ephemeral dance, option negotiation) is explained in how TFTP works and assumed here.

Decide What This Server Is For

Before touching a package manager, name the server's job, because the job decides the settings:

  • Boot server (PXE). Serves bootloaders, kernels, and boot configs. Strictly read-only, needs solid support for the block-size and transfer-size options (boot loaders lean on them — see PXE boot and TFTP), and benefits from living close to the machines that boot from it.
  • Config drop zone. Receives device config backups. This is the one role that needs uploads enabled — the most dangerous setting a TFTP server has, so it gets a fenced inbox and a sweep job, per the workflow in our configs and firmware article.
  • Firmware shelf. Serves images to devices during upgrades. Read-only, occasionally busy with large files, so block-size tuning pays off most here.

One server can do all three, but separating writes from everything else — even just into different directories with different rules — keeps each role's risks from contaminating the others. Wherever it runs, the machine belongs on the management network with the containment described in TFTP security and containment: this article covers making the server work; that one covers keeping it fenced.

As for software: every platform has options. On Linux, tftpd-hpa is the widely packaged standalone classic, and dnsmasq bundles a TFTP server alongside DHCP (convenient for PXE labs). Windows has several service and GUI implementations. The walkthrough below uses tftpd-hpa on a Debian-family system because it is common and its settings map cleanly to concepts every server shares — adapt names and paths to your platform and the ideas carry over.

Installation and First Start

Here is the whole setup, verified at each step. Run it top to bottom and you finish with a server you have proven works — not one you merely hope does:

# 1. Install the server, plus a command-line client for testing
$ sudo apt install tftpd-hpa tftp-hpa

# 2. Look at the settings before changing anything
$ cat /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure"

# 3. Make sure the root exists and belongs to the service account
$ sudo mkdir -p /srv/tftp
$ sudo chown tftp:tftp /srv/tftp

# 4. Restart, then confirm something is listening on UDP port 69
$ sudo systemctl restart tftpd-hpa
$ sudo ss -lun | grep ':69'
UNCONN  0  0  0.0.0.0:69  0.0.0.0:*

# 5. Plant a test file and fetch it through the front door
$ echo "tftp server alive" | sudo tee /srv/tftp/canary.txt
$ cd /tmp && tftp 127.0.0.1 -c get canary.txt && cat canary.txt
tftp server alive

# 6. Repeat the fetch from ANOTHER machine on the device subnet
$ tftp 192.0.2.50 -c get canary.txt

Each line earns its keep. The four settings in step 2 are the server's whole personality: the unprivileged account it runs as, the root directory it serves, the address and port it listens on, and its behavior flags — --secure being the important default, explained in the next section. Step 4's ss check catches the embarrassing class of failure (service not actually running, port taken by something else) before any device does. And step 6 matters more than step 5: a loopback test proves the daemon works, but only a fetch from another machine proves the network lets transfers happen — firewalls do not apply to loopback, so localhost success plus remote failure points straight at filtering.

Remember: always finish a TFTP server change by testing from a real machine on the same network segment the devices use — not from the server itself. The lock-step transfer's reply comes from an ephemeral port, and a firewall that passes the request but drops the replies produces the exact "timeout" a dead server would. Loopback tests cannot see that failure; remote tests cannot miss it.

Root Directory Discipline

The --secure flag is the first thing to understand, because it silently rewrites what every filename means. With it, the server locks itself into the root directory at startup (a chroot, in Unix terms — the directory becomes the daemon's entire visible world), and every path a client requests is interpreted relative to that root. A client asking for boot/loader.efi gets /srv/tftp/boot/loader.efi. This is both a safety property — requests can never escape the tree, no matter how creatively they are written — and the source of the single most classic TFTP configuration error:

The double-path trap. An administrator, knowing the file lives at /srv/tftp/images/fw.bin, configures the client to request exactly that path. The server, secure in its chroot, appends the request to its root and looks for /srv/tftp/srv/tftp/images/fw.bin — which does not exist. The client reports "file not found"; the administrator, staring directly at the existing file, concludes the server is haunted. The rule that dissolves the confusion: clients request paths as seen from inside the root. Here, the correct request is images/fw.bin.

Three more habits keep the root boring in the best sense:

  • Match case exactly. On Linux filesystems, Firmware.BIN and firmware.bin are different files. Devices request literal strings; copy-paste filenames into device configs rather than retyping them.
  • Structure by role. boot/, firmware/, inbox/ — one subdirectory per job, so permissions can differ per job and a directory listing explains itself to the next administrator.
  • Give the root its own filesystem or quota. A config drop zone accepts writes; writes can fill disks; a full disk on a shared filesystem takes the whole host down with it. A modest dedicated volume turns that outage into an error code.

Permissions That Match the Role

The daemon starts as root (it must, to claim port 69) and immediately drops to the unprivileged account from its settings — tftp here. From then on, ordinary file permissions decide everything, evaluated as that account:

  • For downloads: files must be readable by the tftp user. A file copied into the root as root with mode 600 is invisible to clients in the most confusing way — the server is running, the file exists, and every request fails with "access violation" or "file not found" depending on the implementation.
  • For uploads, the rules surprise everyone. Out of the box, tftpd-hpa and its relatives accept an upload only onto a file that already exists and is world-writable. A device pushing sw1.cfg for the first time is refused with "access violation" — the historical safety default. You either pre-create each file (touch sw1.cfg && chmod 666 sw1.cfg — workable for a handful of devices, absurd for fifty) or enable creation properly:
# /etc/default/tftpd-hpa — a drop zone that can accept new files
TFTP_OPTIONS="--secure --create --umask 022"

# Fence writes into one inbox owned by the service account
$ sudo mkdir -p /srv/tftp/inbox
$ sudo chown tftp:tftp /srv/tftp/inbox
$ sudo chmod 755 /srv/tftp/inbox

With --create, new files are allowed wherever the tftp user can write — so the discipline is to make the inbox the only directory it can write to (own the rest of the tree as root, read-only for the service account). That single decision converts "anyone can overwrite my boot files" into "uploads land in one fenced folder that a sweep job empties." If the server's role never includes uploads, skip --create entirely and enjoy a server on which writes are impossible.

Timeouts, Retries, and Block Size

The defaults suit a healthy LAN, which is where most TFTP servers live, so the honest first advice is: tune nothing until a measurement tells you to. When one does, three knobs matter, whatever your server calls them:

  • Retransmission timeout. How long the sender waits for an ACK before resending the last packet (the lock-step's recovery mechanism). Raise it for genuinely slow or long-haul paths where retransmissions are firing for packets that were merely late. Check the units in your server's documentation before touching it — tftpd-hpa's --timeout-family options take microseconds in places where a human expects seconds, and a value six orders of magnitude too small produces a retransmission storm that looks like network failure.
  • Maximum block size. Clients negotiate block size upward via the blksize option; the server enforces a ceiling (--blocksize in tftpd-hpa). The sweet spot on standard Ethernet is 1,468 bytes or slightly lower: block plus TFTP, UDP, and IP headers then fits inside one 1,500-byte frame. Allow much more and each block gets fragmented — split across multiple IP packets that must all arrive to count — which some minimal boot-time network stacks handle poorly, producing transfers that start briskly and then stall. If a PXE client dies partway through a kernel download, an over-generous block size is a leading suspect.
  • Ephemeral port range. Some servers (tftpd-hpa: --port-range 40000:40100) can confine the transfer-side ports the protocol picks after the initial request to a fixed range. Operationally golden when strict or stateless firewalls stand between clients and server: instead of "allow high UDP ports," the rule becomes "allow 40000–40100 to this one host."

One non-knob worth restating: TFTP will never be fast over distance. One block per round trip is the protocol's nature; a bigger block softens it, and nothing removes it. When someone asks you to "fix the slow TFTP transfer" across a WAN, the correct fixes are a server nearer the clients or a heavier protocol — the arithmetic is in the protocol article.

The Classic Troubleshooting Cases

When a device-side transfer fails, resist the urge to squint at the device's two-word error. Go to the server and run the same short diagnostic ladder every time — it localizes almost any TFTP failure in a few minutes:

# 1. Is the service alive and listening?
$ systemctl status tftpd-hpa
$ sudo ss -lunp | grep ':69'

# 2. Watch requests arrive as the device retries (logging tells all)
$ sudo journalctl -u tftpd-hpa -f
#    no line when the device tries?  -> the request never arrived: network/firewall
#    "RRQ from ..." then silence?    -> replies not getting back: filtering, ports
#    an error in the log?            -> the log names the file and the reason

# 3. Watch the wire itself: the request and the ephemeral-port replies
$ sudo tcpdump -ni any 'udp port 69 or (udp and portrange 32768-60999)'

# 4. Reproduce with a normal client from the device's subnet
$ tftp 192.0.2.50 -c get images/fw.bin

# 5. Check what the DAEMON can see, as the account it runs as
$ sudo -u tftp ls -l /srv/tftp/images/
$ sudo -u tftp head -c1 /srv/tftp/images/fw.bin >/dev/null && echo readable

Step 5 is the underused one: testing as the service account catches every permission and path problem in one command, including the ones invisible when you look as root. With the ladder's evidence in hand, the classic cases map to fixes almost mechanically:

Symptom Usual cause Fix
"File not found," but you can see the file Double-path trap (client sent the full server-side path), or a case mismatch Request the path as seen from inside the root; copy-paste exact filenames
"Access violation" on download File not readable by the service account; sometimes a security framework (SELinux/AppArmor) denying quietly Fix ownership/mode (step 5 proves it); check the audit log if permissions look right
"Access violation" on upload Server refuses new files by default (no --create), or inbox not writable by the service account Enable --create with a fenced inbox, or pre-create the target file
Timeout; nothing works at all Service down, wrong server IP on the device, or firewall dropping the request or the ephemeral-port replies Ladder steps 1–3 split it: no log line = network in; log line then silence = replies out
Transfer starts, then stalls partway Fragmentation from an oversized negotiated block, or a lossy path outrunning the timeout Cap block size near 1,468; revisit timeout; test the path for loss
Upload "succeeded" but the file is empty or short Transfer died mid-write; TFTP leaves the partial file with no warning Verify sizes after every upload; make the sweep job refuse files that end abruptly

Keep It Healthy

A TFTP server that matters — and if devices boot or back up through it, it matters — deserves the same modest operational care as any other service:

  • Leave verbose logging on and ship the logs. The per-request log lines are your only visibility into an unauthenticated service, for troubleshooting and for security alike (the alerting side is covered in the containment article).
  • Run a canary fetch on a schedule. A monitoring job that TFTP-fetches a small known file every few minutes, from a machine on the device subnet, catches dead services and broken firewall paths before a midnight upgrade does. This is the single highest-value habit on this list.
  • Watch the disk if uploads are enabled, and alert well before full.
  • Inventory the tree. A nightly listing (or hash) of the root, diffed against yesterday's, catches both intruders and colleagues who "just added a file for a minute."
  • Write the runbook line. Root path, options, roles of each subdirectory, where the logs go, how the canary works. Five lines in the wiki turns your careful setup into the team's careful setup.

The Server in One Paragraph

Give the server one named job, install it with the walkthrough above, and prove it with a remote fetch before any device depends on it. Serve a chrooted root where client paths are relative, case matters, and the double-path trap is dissolved by rule rather than rediscovered by outage. Let permissions be evaluated as the unprivileged service account, enable file creation only into a fenced inbox — or not at all — and tune only the three knobs that matter: retransmission timeout, block-size ceiling, port range. When something fails, run the diagnostic ladder and let the log, the wire, and the as-the-daemon file test point at the layer that lied.

With the server solid, the natural companions are the config and firmware procedures that will use it, the PXE chain if it serves boots, and — before anything touches production — the containment posture that keeps a defenseless protocol out of trouble.

Frequently Asked Questions

Why does the server say "file not found" when the file is right there?
Usually the double-path trap: the client requested the full server-side path, and the chrooted server appended it to its root, looking for a doubled path that does not exist. Request paths relative to the TFTP root instead. The other frequent culprit is a simple case mismatch in the filename.
How do I let devices upload backups to my server?
By default most servers only accept uploads onto pre-existing world-writable files. Enable file creation (the --create flag in tftpd-hpa) and make one inbox directory writable by the service account, keeping the rest of the tree read-only. Then sweep the inbox to managed storage on a schedule.
Which firewall ports does a TFTP server need open?
UDP 69 inbound for requests, plus the ephemeral ports the transfers continue on — the replies come from a new port, not 69. A stateful firewall with TFTP awareness handles that automatically; otherwise, configure the server's port-range option to a fixed range and allow exactly that.
Can several devices transfer at the same time?
Yes. Each transfer runs on its own pair of ports (its transfer identifiers), so a single server can feed many clients concurrently without mixing them up — a whole rack PXE-booting from one small server is routine. The practical limits are disk speed and the network, not the protocol.
Does the TFTP server need to run as root?
It needs root only for one instant, to bind port 69, and immediately drops to an unprivileged account — the account whose file permissions then govern everything. If reads or writes fail mysteriously, test the files as that account, not as root.
Is a Windows or a Linux TFTP server better?
The protocol is identical, so choose by operational fit: where your monitoring lives, what your team maintains comfortably, and what your deployment tooling expects. The disciplines in this article — chrooted root, service-account permissions, logging, canary fetches — apply on either platform.

From the Sysax team: we build secure file transfer software for Windows — Sysax Multi Server, an FTP, FTPS, SFTP, and HTTPS server, and Sysax FTP Automation for scheduled, scripted transfers. Free trials are on the download page.