TFTP Server in VyOS
TFTP (Trivial File Transfer Protocol) is a simple file transfer protocol widely used to deliver configurations, firmware, and images to network hardware. VyOS can act as a TFTP server for centralized management of device configurations.
Key features
- Simplicity: Minimal client requirements (no authentication)
- PXE Boot: Network boot of operating systems (diskless workstations)
- Network hardware: Delivery of configurations and firmware to switches, routers, and IP phones
- Configuration backup: Centralized storage of device configurations
- Low requirements: Runs over UDP and requires no connection setup
- DHCP integration: Automatic client provisioning via DHCP option 66/67
TFTP protocol characteristics
Advantages:
- Simple implementation
- Minimal resource requirements
- Supported by all network devices
- Works in a pre-boot environment (PXE)
Limitations:
- No authentication (only IP-based ACLs)
- No encryption (data sent in the clear)
- Maximum file size: 32 MB (extended mode up to 4 GB)
- Only file reads and writes (no directory listing)
- UDP port 69
Security recommendations:
- Use only in trusted networks (management VLAN)
- Restrict access with firewall ACLs
- Do not store confidential data
- Use read-only mode where possible
Basic configuration
Simple TFTP server
# Enable the TFTP server
set service tftp-server listen-address 192.168.1.1
# File directory (default is /srv/tftp)
set service tftp-server directory /config/tftp
# Allow file uploads to the server
set service tftp-server allow-upload
commit
saveVerification:
# TFTP server status
show service tftp-server
# Check the port
show system connections port 69
# List files in the TFTP directory
ls -lh /config/tftpTesting from a client:
# Linux
tftp 192.168.1.1
tftp> get test.txt
tftp> put backup.cfg
tftp> quit
# Or as a single command
tftp -m binary 192.168.1.1 -c get test.txt
# Windows
tftp -i 192.168.1.1 get test.txt
tftp -i 192.168.1.1 put backup.cfgTFTP with access control
# TFTP server
set service tftp-server listen-address 192.168.100.1
set service tftp-server directory /config/tftp
# Allow read-only access (disable upload)
delete service tftp-server allow-upload
# Firewall - allow access only from the management network
set firewall group network-group MGMT_NETWORKS network 192.168.100.0/24
set firewall group network-group MGMT_NETWORKS network 10.10.10.0/24
set firewall name MGMT_LOCAL rule 100 action accept
set firewall name MGMT_LOCAL rule 100 protocol udp
set firewall name MGMT_LOCAL rule 100 destination port 69
set firewall name MGMT_LOCAL rule 100 source group network-group MGMT_NETWORKS
# Apply the firewall to the interface
set firewall interface eth1 local name MGMT_LOCAL
commit
saveCreating a directory structure
# Create directories for different device types
sudo mkdir -p /config/tftp/{configs,firmware,pxe,phones}
sudo chown -R tftp:tftp /config/tftp
sudo chmod -R 755 /config/tftp
# Uploads require write permissions
sudo chmod 777 /config/tftp/configsDirectory structure:
/config/tftp/
├── configs/ # Device configurations
│ ├── switches/
│ ├── routers/
│ └── wireless/
├── firmware/ # Firmware
│ ├── cisco/
│ ├── mikrotik/
│ └── ubiquiti/
├── pxe/ # PXE boot images
│ ├── pxelinux.0
│ ├── kernel
│ └── initrd
└── phones/ # IP phone configurations
├── yealink/
└── grandstream/Advanced configuration
DHCP integration for PXE Boot
# TFTP server
set service tftp-server listen-address 192.168.1.1
set service tftp-server directory /config/tftp/pxe
# DHCP server with PXE options
set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 range 0 start 192.168.1.100
set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 range 0 stop 192.168.1.200
set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 option default-router 192.168.1.1
set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 option name-server 192.168.1.1
# DHCP Option 66 - TFTP server IP
set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 option tftp-server-name 192.168.1.1
# DHCP Option 67 - Boot filename
set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 option bootfile-name 'pxelinux.0'
commit
saveTFTP for IP phones
# TFTP for Yealink and Grandstream phones
set service tftp-server listen-address 192.168.10.1
set service tftp-server directory /config/tftp/phones
# DHCP for IP phones (separate VLAN)
set service dhcp-server shared-network-name VOICE subnet 192.168.10.0/24 range 0 start 192.168.10.100
set service dhcp-server shared-network-name VOICE subnet 192.168.10.0/24 range 0 stop 192.168.10.200
set service dhcp-server shared-network-name VOICE subnet 192.168.10.0/24 option default-router 192.168.10.1
set service dhcp-server shared-network-name VOICE subnet 192.168.10.0/24 option name-server 192.168.10.1
# TFTP server for the phones
set service dhcp-server shared-network-name VOICE subnet 192.168.10.0/24 option tftp-server-name 192.168.10.1
# Option 66 (alternative syntax)
set service dhcp-server shared-network-name VOICE subnet 192.168.10.0/24 option tftp-server 192.168.10.1
commit
savePreparing phone configurations:
# Yealink - automatic provisioning
sudo tee /config/tftp/phones/y000000000000.cfg > /dev/null <<'EOF'
#!version:1.0.0.1
account.1.enable = 1
account.1.label = Office
account.1.display_name = John Doe
account.1.auth_name = 1001
account.1.user_name = 1001
account.1.password = SecretPassword
account.1.sip_server.1.address = pbx.example.com
account.1.sip_server.1.port = 5060
EOF
# Grandstream
sudo tee /config/tftp/phones/cfg000b82000000 > /dev/null <<'EOF'
## Grandstream Config
P47 = pbx.example.com
P35 = 1001
P36 = SecretPassword
P34 = 1001
P3 = 5060
EOF
sudo chown tftp:tftp /config/tftp/phones/*.cfgTFTP for Cisco network hardware
# TFTP server for Cisco devices
set service tftp-server listen-address 10.0.0.1
set service tftp-server directory /config/tftp/cisco
set service tftp-server allow-upload
# Firewall - allow access only from Cisco devices
set firewall group network-group CISCO_SWITCHES network 10.0.1.0/24
set firewall group network-group CISCO_SWITCHES network 10.0.2.0/24
set firewall name CISCO_MGMT_LOCAL rule 50 action accept
set firewall name CISCO_MGMT_LOCAL rule 50 protocol udp
set firewall name CISCO_MGMT_LOCAL rule 50 destination port 69
set firewall name CISCO_MGMT_LOCAL rule 50 source group network-group CISCO_SWITCHES
set firewall interface eth0 local name CISCO_MGMT_LOCAL
commit
saveBacking up a Cisco configuration:
# On the Cisco device
Switch# copy running-config tftp:
Address or name of remote host []? 10.0.0.1
Destination filename [switch-confg]? switch-01-backup.cfg
# Restoring the configuration
Switch# copy tftp: running-config
Address or name of remote host []? 10.0.0.1
Source filename []? switch-01-backup.cfgBackup automation
# Script for automatic backup of Cisco configurations
sudo tee /config/scripts/tftp-backup-cisco.sh > /dev/null <<'EOF'
#!/bin/bash
# Automatic backup of Cisco configurations over TFTP
TFTP_DIR="/config/tftp/cisco/backups"
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR="$TFTP_DIR/$DATE"
mkdir -p "$BACKUP_DIR"
# Device list (IP:hostname)
DEVICES=(
"10.0.1.10:core-switch-01"
"10.0.1.11:core-switch-02"
"10.0.2.10:access-switch-01"
)
for device in "${DEVICES[@]}"; do
IP="${device%%:*}"
NAME="${device##*:}"
echo "Backing up $NAME ($IP)..."
# Use expect for automation (requires expect to be installed)
/usr/bin/expect <<EXPECT_EOF
spawn ssh admin@$IP
expect "Password:"
send "YourPassword\r"
expect "#"
send "copy running-config tftp://10.0.0.1/$DATE/$NAME.cfg\r"
expect "Address or name of remote host"
send "\r"
expect "Destination filename"
send "\r"
expect "#"
send "exit\r"
EXPECT_EOF
echo "Backup completed: $NAME"
done
# Remove old backups (older than 30 days)
find "$TFTP_DIR" -type d -mtime +30 -exec rm -rf {} \;
echo "All backups completed on $DATE"
EOF
sudo chmod +x /config/scripts/tftp-backup-cisco.sh
# Schedule a daily backup at 3 AM
set system task-scheduler task cisco-backup interval '0 3 * * *'
set system task-scheduler task cisco-backup executable path '/config/scripts/tftp-backup-cisco.sh'
commit
saveConfiguration examples
1. PXE Boot server for diskless workstations
Goal: Configure VyOS as a PXE server for booting Linux over the network.
# TFTP server for PXE
set service tftp-server listen-address 192.168.1.1
set service tftp-server directory /config/tftp/pxe
# DHCP with PXE options
set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 range 0 start 192.168.1.50
set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 range 0 stop 192.168.1.150
set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 option default-router 192.168.1.1
set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 option name-server 192.168.1.1
set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 option tftp-server-name 192.168.1.1
set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 option bootfile-name 'pxelinux.0'
commit
savePreparing the PXE environment:
# Install the required packages
sudo mkdir -p /config/tftp/pxe/{pxelinux.cfg,images}
# Download PXELINUX
wget https://mirrors.edge.kernel.org/pub/linux/utils/boot/syslinux/syslinux-6.03.tar.gz
tar -xzf syslinux-6.03.tar.gz
sudo cp syslinux-6.03/bios/core/pxelinux.0 /config/tftp/pxe/
sudo cp syslinux-6.03/bios/com32/elflink/ldlinux/ldlinux.c32 /config/tftp/pxe/
sudo cp syslinux-6.03/bios/com32/menu/vesamenu.c32 /config/tftp/pxe/
sudo cp syslinux-6.03/bios/com32/libutil/libutil.c32 /config/tftp/pxe/
# Download Ubuntu Live for PXE
cd /config/tftp/pxe/images
wget http://archive.ubuntu.com/ubuntu/dists/jammy/main/installer-amd64/current/legacy-images/netboot/netboot.tar.gz
tar -xzf netboot.tar.gz
# Create the boot menu
sudo tee /config/tftp/pxe/pxelinux.cfg/default > /dev/null <<'EOF'
DEFAULT vesamenu.c32
TIMEOUT 300
PROMPT 0
MENU TITLE PXE Boot Menu
LABEL ubuntu-install
MENU LABEL Install Ubuntu 22.04 Server
KERNEL images/ubuntu-installer/linux
APPEND vga=788 initrd=images/ubuntu-installer/initrd.gz
LABEL ubuntu-live
MENU LABEL Ubuntu 22.04 Live (no install)
KERNEL images/ubuntu-live/vmlinuz
APPEND initrd=images/ubuntu-live/initrd boot=casper netboot=nfs nfsroot=192.168.1.1:/srv/nfs/ubuntu-live
LABEL local
MENU LABEL Boot from local disk
LOCALBOOT 0
EOF
sudo chown -R tftp:tftp /config/tftp/pxeTesting PXE:
- Power on the client machine
- Enable PXE/Network Boot in the BIOS
- Set the network card as the first boot device
- Reboot - the PXE menu should appear
2. TFTP for centralized backup of Mikrotik configurations
Goal: Automatic backup of Mikrotik router configurations.
# TFTP server for Mikrotik
set service tftp-server listen-address 10.0.0.1
set service tftp-server directory /config/tftp/mikrotik
set service tftp-server allow-upload
# Firewall
set firewall group network-group MIKROTIK_DEVICES network 10.0.1.0/24
set firewall name MIKROTIK_MGMT_LOCAL rule 60 action accept
set firewall name MIKROTIK_MGMT_LOCAL rule 60 protocol udp
set firewall name MIKROTIK_MGMT_LOCAL rule 60 destination port 69
set firewall name MIKROTIK_MGMT_LOCAL rule 60 source group network-group MIKROTIK_DEVICES
set firewall interface eth0 local name MIKROTIK_MGMT_LOCAL
commit
saveCreate the backup structure:
sudo mkdir -p /config/tftp/mikrotik/backups
sudo chmod 777 /config/tftp/mikrotik/backupsMikrotik script for automatic backup:
# Mikrotik RouterOS script
/system script add name=backup-to-tftp source={
/system backup save name=backup-latest
:delay 5s
/tool fetch address=10.0.0.1 src-path=backup-latest.backup dst-path=backups/[/system identity get name]-[/system clock get date].backup upload=yes mode=tftp
}
# Schedule a daily backup at 4 AM
/system scheduler add name=daily-backup interval=1d start-time=04:00:00 on-event=backup-to-tftpMonitoring backups on VyOS:
# Script to check backup freshness
sudo tee /config/scripts/check-mikrotik-backups.sh > /dev/null <<'EOF'
#!/bin/bash
BACKUP_DIR="/config/tftp/mikrotik/backups"
TODAY=$(date +%Y-%m-%d)
YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
echo "Checking Mikrotik backups..."
for device in router-01 router-02 router-03; do
LATEST=$(find "$BACKUP_DIR" -name "$device-*" -mtime -1 | wc -l)
if [ "$LATEST" -eq 0 ]; then
echo "WARNING: No recent backup for $device"
# Send an alert (email, telegram, syslog)
logger -t mikrotik-backup "WARNING: Missing backup for $device"
else
echo "OK: $device backup found"
fi
done
EOF
sudo chmod +x /config/scripts/check-mikrotik-backups.sh
# Schedule the check every morning at 8:00
set system task-scheduler task check-backups interval '0 8 * * *'
set system task-scheduler task check-backups executable path '/config/scripts/check-mikrotik-backups.sh'
commit
save3. TFTP for Yealink IP phones in an office
Goal: Centralized configuration management for 50 Yealink IP phones.
# TFTP for the Voice VLAN
set service tftp-server listen-address 192.168.10.1
set service tftp-server directory /config/tftp/yealink
# DHCP for the Voice VLAN (802.1Q VLAN 10)
set service dhcp-server shared-network-name VOICE subnet 192.168.10.0/24 range 0 start 192.168.10.100
set service dhcp-server shared-network-name VOICE subnet 192.168.10.0/24 range 0 stop 192.168.10.200
set service dhcp-server shared-network-name VOICE subnet 192.168.10.0/24 option default-router 192.168.10.1
set service dhcp-server shared-network-name VOICE subnet 192.168.10.0/24 option name-server 192.168.10.1
set service dhcp-server shared-network-name VOICE subnet 192.168.10.0/24 option tftp-server-name 192.168.10.1
# VLAN for Voice
set interfaces ethernet eth1 vif 10 address 192.168.10.1/24
set interfaces ethernet eth1 vif 10 description 'Voice VLAN'
commit
saveCreate a shared configuration file for all phones:
# Common configuration for all Yealink phones (y000000000000.cfg)
sudo tee /config/tftp/yealink/y000000000000.cfg > /dev/null <<'EOF'
#!version:1.0.0.1
## Common settings
lang.gui = Russian
lang.wui = Russian
local_time.time_zone = +3
local_time.ntp_server1 = pool.ntp.org
local_time.dhcp_time = 0
## SIP server
account.1.sip_server.1.address = pbx.corp.local
account.1.sip_server.1.port = 5060
account.1.outbound_proxy.1.address = pbx.corp.local
account.1.outbound_proxy.1.port = 5060
## Automatic provisioning
auto_provision.mode = 6
auto_provision.schedule.periodic_minute = 1440
auto_provision.server.url = http://192.168.10.1/provision/
## Voice quality
voice.codec.1.enable = 1
voice.codec.1.payload_type = PCMU
voice.codec.2.enable = 1
voice.codec.2.payload_type = PCMA
voice.codec.3.enable = 1
voice.codec.3.payload_type = G722
## Network settings
network.vlan.internet_port_enable = 1
network.vlan.internet_port_vid = 10
network.vlan.internet_port_priority = 5
network.lldp.enable = 1
## Security
security.user_password = admin:AdminPassword123
## Programmable keys
programablekey.1.type = 15
programablekey.1.line = 1
programablekey.1.value = Voicemail
programablekey.1.label = Voicemail
## Wallpaper and screensaver
wallpaper.file_name = /config/tftp/yealink/wallpaper.jpg
screensaver.file_name = /config/tftp/yealink/screensaver.jpg
EOF
sudo chown tftp:tftp /config/tftp/yealink/y000000000000.cfgPer-MAC-address individual configurations:
# Example for a specific phone (MAC: 00:15:65:12:34:56)
sudo tee /config/tftp/yealink/001565123456.cfg > /dev/null <<'EOF'
#!version:1.0.0.1
# Inherit the common configuration
include:config y000000000000.cfg
## Individual settings for this specific phone
account.1.enable = 1
account.1.label = Reception
account.1.display_name = Reception desk
account.1.auth_name = 1000
account.1.user_name = 1000
account.1.password = SecretPass1000
## BLF keys (monitor other lines)
programablekey.2.type = 16
programablekey.2.line = 1
programablekey.2.value = 1001
programablekey.2.label = Director
programablekey.3.type = 16
programablekey.3.line = 1
programablekey.3.value = 1002
programablekey.3.label = Accounting
EOF
sudo chown tftp:tftp /config/tftp/yealink/*.cfgBulk deployment via script:
# Script to generate configurations from CSV
sudo tee /config/scripts/generate-yealink-configs.sh > /dev/null <<'EOF'
#!/bin/bash
# Generate Yealink configurations from a CSV file
# CSV format: MAC,Extension,Name,Password
CSV_FILE="/config/tftp/yealink/phones.csv"
CONFIG_DIR="/config/tftp/yealink"
while IFS=, read -r mac ext name password; do
# Skip the header
[[ "$mac" == "MAC" ]] && continue
# Strip the colons from the MAC
mac_clean=$(echo "$mac" | tr -d ':' | tr '[:upper:]' '[:lower:]')
cat > "$CONFIG_DIR/${mac_clean}.cfg" <<CONFIG
#!version:1.0.0.1
include:config y000000000000.cfg
account.1.enable = 1
account.1.label = $name
account.1.display_name = $name
account.1.auth_name = $ext
account.1.user_name = $ext
account.1.password = $password
CONFIG
echo "Generated config for $name ($ext) - MAC: $mac"
done < "$CSV_FILE"
chown tftp:tftp "$CONFIG_DIR"/*.cfg
echo "All phone configs generated"
EOF
sudo chmod +x /config/scripts/generate-yealink-configs.shThe phones.csv file:
MAC,Extension,Name,Password
00:15:65:12:34:56,1000,Reception,Pass1000
00:15:65:12:34:57,1001,Director,Pass1001
00:15:65:12:34:58,1002,Accounting,Pass1002
00:15:65:12:34:59,1003,Sales,Pass1003Running the generation:
sudo /config/scripts/generate-yealink-configs.sh4. TFTP with HTTP provisioning for Cisco devices
Goal: Combined use of TFTP and HTTP for Cisco devices.
# TFTP for legacy devices
set service tftp-server listen-address 10.0.0.1
set service tftp-server directory /config/tftp/cisco
# HTTP for modern devices
set service https listen-address 10.0.0.1
set service https allow-client address 10.0.0.0/8
commit
saveCreate the HTTP provisioning structure:
# Create the web directory for provisioning
sudo mkdir -p /var/www/html/cisco/{configs,firmware,scripts}
sudo ln -s /config/tftp/cisco /var/www/html/cisco/legacy
# Create index.html for navigation
sudo tee /var/www/html/cisco/index.html > /dev/null <<'EOF'
<!DOCTYPE html>
<html>
<head>
<title>Cisco Device Provisioning</title>
</head>
<body>
<h1>Cisco Provisioning Server</h1>
<ul>
<li><a href="configs/">Configurations</a></li>
<li><a href="firmware/">Firmware Images</a></li>
<li><a href="scripts/">Scripts</a></li>
<li><a href="legacy/">TFTP Files (Legacy)</a></li>
</ul>
</body>
</html>
EOFScript for automatic Cisco provisioning:
# ZTP (Zero Touch Provisioning) script
sudo tee /var/www/html/cisco/scripts/ztp.py > /dev/null <<'EOF'
#!/usr/bin/env python3
import cli
import sys
# Base configuration for a new Cisco device
commands = [
'hostname new-cisco-switch',
'ip domain-name corp.local',
'username admin privilege 15 secret AdminPassword123',
'enable secret EnablePassword123',
'ip default-gateway 10.0.0.1',
'ip name-server 10.0.0.1',
'ntp server 10.0.0.1',
'logging host 10.0.0.1',
'snmp-server community public RO',
'snmp-server host 10.0.0.1 version 2c public',
'line vty 0 4',
'transport input ssh',
'login local',
'exit',
'interface Vlan1',
'ip address dhcp',
'no shutdown',
'exit',
'crypto key generate rsa modulus 2048',
]
print("Starting Zero Touch Provisioning...")
for cmd in commands:
try:
cli.configure(cmd)
print(f"OK: {cmd}")
except Exception as e:
print(f"ERROR: {cmd} - {e}")
print("Saving configuration...")
cli.execute('copy running-config startup-config')
print("ZTP completed successfully")
EOF
sudo chmod +x /var/www/html/cisco/scripts/ztp.pyDHCP with options for ZTP:
# DHCP for Cisco with ZTP
set service dhcp-server shared-network-name CISCO_MGMT subnet 10.0.0.0/24 range 0 start 10.0.0.100
set service dhcp-server shared-network-name CISCO_MGMT subnet 10.0.0.0/24 range 0 stop 10.0.0.200
set service dhcp-server shared-network-name CISCO_MGMT subnet 10.0.0.0/24 option default-router 10.0.0.1
set service dhcp-server shared-network-name CISCO_MGMT subnet 10.0.0.0/24 option name-server 10.0.0.1
# DHCP Option 150 - TFTP server (Cisco-specific)
set service dhcp-server shared-network-name CISCO_MGMT subnet 10.0.0.0/24 option tftp-server-name 10.0.0.1
# DHCP Option 67 - Boot file for ZTP
set service dhcp-server shared-network-name CISCO_MGMT subnet 10.0.0.0/24 option bootfile-name 'http://10.0.0.1/cisco/scripts/ztp.py'
commit
save5. TFTP with backup rotation and archiving
Goal: Automatic archiving of old backups with rotation.
# TFTP server
set service tftp-server listen-address 192.168.1.1
set service tftp-server directory /config/tftp
set service tftp-server allow-upload
commit
saveScript for rotation and archiving:
sudo tee /config/scripts/tftp-backup-rotation.sh > /dev/null <<'EOF'
#!/bin/bash
# Automatic TFTP backup rotation with archiving
TFTP_DIR="/config/tftp"
ARCHIVE_DIR="/var/backups/tftp-archives"
RETENTION_DAYS=90
DATE=$(date +%Y%m%d)
ARCHIVE_NAME="tftp-backup-$DATE.tar.gz"
# Create the archive directory
mkdir -p "$ARCHIVE_DIR"
echo "Starting TFTP backup rotation..."
# 1. Archive current backups older than 7 days
find "$TFTP_DIR" -name "*.cfg" -mtime +7 -type f | while read file; do
echo "Archiving: $file"
# Recreate the directory structure inside the archive
relative_path="${file#$TFTP_DIR/}"
archive_file="$ARCHIVE_DIR/$(dirname $relative_path)"
mkdir -p "$archive_file"
# Compress and move
gzip -c "$file" > "$archive_file/$(basename $file).gz"
rm "$file"
done
# 2. Create a consolidated monthly archive
if [ $(date +%d) -eq 01 ]; then
LAST_MONTH=$(date -d "last month" +%Y%m)
echo "Creating monthly archive for $LAST_MONTH..."
tar -czf "$ARCHIVE_DIR/tftp-monthly-$LAST_MONTH.tar.gz" \
-C "$ARCHIVE_DIR" \
$(find "$ARCHIVE_DIR" -name "tftp-backup-${LAST_MONTH}*.tar.gz" -type f -printf "%f ")
# Remove the daily archives after creating the monthly one
find "$ARCHIVE_DIR" -name "tftp-backup-${LAST_MONTH}*.tar.gz" -type f -delete
fi
# 3. Remove archives older than the retention period
find "$ARCHIVE_DIR" -name "tftp-monthly-*.tar.gz" -mtime +$RETENTION_DAYS -type f -delete
# 4. Statistics
TOTAL_SIZE=$(du -sh "$ARCHIVE_DIR" | cut -f1)
ARCHIVE_COUNT=$(find "$ARCHIVE_DIR" -type f | wc -l)
echo "Backup rotation completed"
echo "Total archives: $ARCHIVE_COUNT"
echo "Total size: $TOTAL_SIZE"
# Log to syslog
logger -t tftp-rotation "Backup rotation completed: $ARCHIVE_COUNT archives, $TOTAL_SIZE total"
EOF
sudo chmod +x /config/scripts/tftp-backup-rotation.sh
# Schedule daily rotation at 5 AM
set system task-scheduler task tftp-rotation interval '0 5 * * *'
set system task-scheduler task tftp-rotation executable path '/config/scripts/tftp-backup-rotation.sh'
commit
save6. Monitoring the TFTP server with alerting
Goal: Monitor the availability and activity of the TFTP server.
# TFTP server
set service tftp-server listen-address 192.168.1.1
set service tftp-server directory /config/tftp
# SNMP for monitoring
set service snmp community public authorization ro
set service snmp listen-address 192.168.1.1
commit
saveMonitoring script with Telegram alerts:
sudo tee /config/scripts/tftp-monitor.sh > /dev/null <<'EOF'
#!/bin/bash
# TFTP server monitoring with Telegram alerts
TFTP_DIR="/config/tftp"
TELEGRAM_BOT_TOKEN="YOUR_BOT_TOKEN"
TELEGRAM_CHAT_ID="YOUR_CHAT_ID"
send_telegram() {
local message="$1"
curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" \
-d "chat_id=$TELEGRAM_CHAT_ID" \
-d "text=$message" \
-d "parse_mode=Markdown" > /dev/null
}
# 1. Check whether TFTP is running
if ! systemctl is-active --quiet tftpd-hpa; then
send_telegram "ALERT: TFTP service is DOWN on $(hostname)"
logger -t tftp-monitor "CRITICAL: TFTP service is not running"
exit 1
fi
# 2. Check port availability
if ! netstat -ln | grep -q ':69 '; then
send_telegram "ALERT: TFTP port 69 is not listening on $(hostname)"
logger -t tftp-monitor "CRITICAL: TFTP port not listening"
exit 1
fi
# 3. Check disk space
DISK_USAGE=$(df -h "$TFTP_DIR" | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$DISK_USAGE" -gt 85 ]; then
send_telegram "WARNING: TFTP disk usage is ${DISK_USAGE}% on $(hostname)"
logger -t tftp-monitor "WARNING: High disk usage: ${DISK_USAGE}%"
fi
# 4. Check activity (files in the last 24 hours)
RECENT_FILES=$(find "$TFTP_DIR" -type f -mtime -1 | wc -l)
if [ "$RECENT_FILES" -eq 0 ]; then
send_telegram "INFO: No TFTP activity in last 24 hours on $(hostname)"
logger -t tftp-monitor "INFO: No TFTP activity detected"
fi
# 5. Statistics
TOTAL_FILES=$(find "$TFTP_DIR" -type f | wc -l)
TOTAL_SIZE=$(du -sh "$TFTP_DIR" | cut -f1)
logger -t tftp-monitor "OK: TFTP is running - $TOTAL_FILES files, $TOTAL_SIZE total"
EOF
sudo chmod +x /config/scripts/tftp-monitor.sh
# Schedule the check every 15 minutes
set system task-scheduler task tftp-health-check interval '*/15 * * * *'
set system task-scheduler task tftp-health-check executable path '/config/scripts/tftp-monitor.sh'
commit
saveMonitoring and diagnostics
Operational commands
# TFTP server status
show service tftp-server
# Active TFTP connections
show system connections port 69
# TFTP logs (in syslog)
show log | match tftp
# List files in the TFTP directory
ls -lh /config/tftp
# Disk space usage
df -h /config
# Most recently modified files (uploads)
find /config/tftp -type f -mtime -1 -ls
# Top 10 largest files
du -ah /config/tftp | sort -rh | head -10Activity analysis
# Script to analyze TFTP logs
sudo tee /config/scripts/tftp-stats.sh > /dev/null <<'EOF'
#!/bin/bash
# TFTP usage statistics
echo "TFTP Activity Statistics"
echo "========================"
# Number of requests today
TODAY_REQUESTS=$(journalctl -u tftpd-hpa --since today | grep -c "RRQ\|WRQ")
echo "Requests today: $TODAY_REQUESTS"
# Top requested files
echo -e "\nTop requested files:"
journalctl -u tftpd-hpa --since "7 days ago" | \
grep "RRQ" | \
awk '{print $NF}' | \
sort | uniq -c | sort -rn | head -10
# Top clients
echo -e "\nTop clients:"
journalctl -u tftpd-hpa --since "7 days ago" | \
grep -oP '\d+\.\d+\.\d+\.\d+' | \
sort | uniq -c | sort -rn | head -10
# Errors
echo -e "\nErrors:"
journalctl -u tftpd-hpa --since "7 days ago" | grep -i "error\|fail" | wc -l
EOF
sudo chmod +x /config/scripts/tftp-stats.shTroubleshooting
1. TFTP server does not respond
Symptoms: Clients get a timeout when trying to connect.
Checks:
# Check the service status
show service tftp-server
# Check the port
show system connections port 69
# Check the firewall
show firewall
# Check the directory
ls -ld /config/tftpSolution:
# Restart TFTP
restart service tftp-server
# Check the directory permissions
sudo chown -R tftp:tftp /config/tftp
sudo chmod -R 755 /config/tftp
# Uploads require write permissions
sudo chmod 777 /config/tftp/uploads
# Check the firewall (allow UDP 69)
set firewall name WAN_LOCAL rule 100 action accept
set firewall name WAN_LOCAL rule 100 protocol udp
set firewall name WAN_LOCAL rule 100 destination port 69
commit
saveTesting:
# From a client
tftp -v 192.168.1.1 -c get test.txt
# Or from VyOS (install tftp-client)
sudo apt-get update
sudo apt-get install tftp-hpa
tftp localhost -c get test.txt2. Upload does not work
Symptoms: Reads work, but writing files fails.
Checks:
# Check whether allow-upload is enabled
show configuration service tftp-server | grep allow-upload
# Check the directory permissions
ls -ld /config/tftpSolution:
# Enable upload
set service tftp-server allow-upload
commit
save
# Grant write permissions
sudo chmod 777 /config/tftp
# Or create a dedicated upload directory
sudo mkdir -p /config/tftp/uploads
sudo chmod 777 /config/tftp/uploads3. PXE Boot does not work
Symptoms: The client gets a DHCP lease, but PXE boot does not start.
Checks:
# Check the DHCP options
show configuration service dhcp-server
# Check the PXE files
ls -l /config/tftp/pxe/pxelinux.0
# Check TFTP
show service tftp-serverSolution:
# Make sure DHCP passes the correct options
set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 option tftp-server-name 192.168.1.1
set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 option bootfile-name 'pxelinux.0'
commit
save
# Verify that pxelinux.0 is present
sudo ls -l /config/tftp/pxe/pxelinux.0
# Permissions must be readable
sudo chmod 644 /config/tftp/pxe/pxelinux.0
# Check the dependencies
sudo ls -l /config/tftp/pxe/{ldlinux.c32,vesamenu.c32}Testing on the client:
- Enable PXE in the BIOS/UEFI
- Check whether the client obtains an IP via DHCP
- Check the TFTP logs:
show log | match tftp
4. Cisco cannot save a configuration to TFTP
Symptoms: The Cisco device returns “Error opening tftp…” when attempting a copy.
Checks:
# Check the firewall (make sure the Cisco device can connect)
show firewall
# Check upload
show configuration service tftp-server | grep allow-upload
# Check permissions
ls -ld /config/tftpSolution:
# 1. Enable upload
set service tftp-server allow-upload
# 2. Create a Cisco backup directory with write permissions
sudo mkdir -p /config/tftp/cisco/backups
sudo chmod 777 /config/tftp/cisco/backups
# 3. Firewall - allow from Cisco devices
set firewall group network-group CISCO_DEVICES network 10.0.1.0/24
set firewall name CISCO_LOCAL rule 50 action accept
set firewall name CISCO_LOCAL rule 50 protocol udp
set firewall name CISCO_LOCAL rule 50 destination port 69
set firewall name CISCO_LOCAL rule 50 source group network-group CISCO_DEVICES
set firewall interface eth0 local name CISCO_LOCAL
commit
saveOn the Cisco device:
# Specify the full path including the subdirectory
Switch# copy running-config tftp://10.0.0.1/cisco/backups/switch01.cfg5. IP phones do not load their configuration
Symptoms: Phones obtain an IP but do not apply the configuration.
Checks:
# Check DHCP option 66
show configuration service dhcp-server | grep tftp
# Check the configuration files
ls -l /config/tftp/yealink/
# TFTP logs
show log | match tftp | tail 50Solution:
# 1. Make sure DHCP passes the TFTP server
set service dhcp-server shared-network-name VOICE subnet 192.168.10.0/24 option tftp-server-name 192.168.10.1
commit
save
# 2. Check the naming convention for Yealink
# Format: MAC address without separators + .cfg
# Example: 001565123456.cfg for MAC 00:15:65:12:34:56
# Check the filename format
ls -l /config/tftp/yealink/*.cfg
# 3. File permissions
sudo chmod 644 /config/tftp/yealink/*.cfg
sudo chown tftp:tftp /config/tftp/yealink/*.cfg
# 4. Check the shared configuration file
ls -l /config/tftp/yealink/y000000000000.cfgOn the phone (via the web interface):
- Settings > Auto Provision
- Server URL: tftp://192.168.10.1
- Click Auto Provision Now
6. Large files are not transferred
Symptoms: Files larger than 32 MB do not transfer or are cut off.
Cause: Standard TFTP has a 32 MB size limit.
Solution:
# 1. Use HTTP/FTP for large files
set service https listen-address 192.168.1.1
# 2. Place the files in the web directory
sudo mkdir -p /var/www/html/firmware
sudo cp large-firmware.bin /var/www/html/firmware/
# 3. On the client, use HTTP instead of TFTP
# Cisco:
Switch# copy http://192.168.1.1/firmware/large-firmware.bin flash:
# Mikrotik:
/tool fetch url=http://192.168.1.1/firmware/large-firmware.bin
# 4. For PXE, use HTTP boot (iPXE)
# Download iPXE
wget http://boot.ipxe.org/ipxe.pxe -O /config/tftp/pxe/ipxe.pxe
# Add to pxelinux.cfg/default
LABEL ipxe
KERNEL ipxe.pxeBest practices
Access security
- Use TFTP only in isolated management VLANs
- Restrict access with firewall ACLs
- Disable upload where it is not required
- Do not store confidential data in TFTP
Directory structure
- Create separate directories for device types
- Use clear file names
- Keep firmware separate from configs
- Create timestamped backups
Backup
- Automate backup of device configurations
- Use rotation and archiving
- Retain archives for at least 90 days
- Test restoring from backup
Monitoring
- Monitor the availability of the TFTP server
- Track disk space
- Log all operations
- Configure alerts for failures
DHCP integration
- Use DHCP option 66/67 for automation
- Specify the IP address, not the hostname, for the TFTP server
- Verify consistency between the DHCP and TFTP configuration
Performance
- Use an SSD for the TFTP directory under heavy load
- Limit the size of the TFTP directory
- Regularly delete old files
- Monitor network throughput
Documentation
- Maintain a device inventory with MAC addresses
- Document naming conventions for files
- Record firmware versions and update dates
- Create a runbook for common operations
For large files
- Use HTTP/FTP instead of TFTP for files larger than 32 MB
- Enable the HTTP server on VyOS for firmware
- Use iPXE for HTTP boot
Automation
- Use scripts for bulk operations
- Automate the generation of configurations
- Schedule recurring tasks via task-scheduler
- Use Ansible for management
Testing
- Test configurations on test devices
- Verify backups before applying them in production
- Have a rollback plan for firmware updates
- Document verified configurations
Conclusion
The TFTP server in VyOS provides a simple and effective way to centrally manage the configurations and firmware of network hardware. Proper TFTP configuration with backup automation, DHCP integration, and reliable monitoring significantly simplifies network infrastructure administration and improves operational reliability.