To enable FTP on a Raspberry Pi, install the vsftpd (Very Secure FTP Daemon) package via sudo apt install vsftpd, modify the /etc/vsftpd.conf file to allow write access and chroot jails, and restart the service. However, if your goal is simply to transfer files securely without configuring extra ports, SFTP (SSH File Transfer Protocol) is already enabled on port 22 out-of-the-box and requires zero additional setup.
While SFTP is the modern standard, legacy FTP is still required for specific industrial PLCs, older CNC machines, and legacy embedded sensors that only support plain FTP. This guide covers the exact configuration for a robust vsftpd server on the Raspberry Pi 5 (8GB) running Raspberry Pi OS (Bookworm), alongside a hardware GPIO status monitor to visualize your server state on the bench.
Protocol Showdown: FTP vs SFTP vs FTPS on the Pi
Before opening ports on your network, you need to choose the right protocol. Plain FTP sends credentials in cleartext, which is a severe security risk on any internet-facing network. Here is how the three main file transfer protocols compare regarding performance and security on the Raspberry Pi 5's Broadcom BCM2712 SoC.
| Protocol | Default Ports | Encryption | Pi 5 CPU Overhead | Setup Complexity |
|---|---|---|---|---|
| Plain FTP | 20 (Data), 21 (Command) | None (Cleartext) | < 1% | Low (vsftpd) |
| SFTP | 22 (SSH) | AES-256 (SSH Tunnel) | ~3-5% | Zero (Built-in) |
| FTPS (Explicit) | 21, 990, + Passive Range | TLS 1.2/1.3 | ~4-6% | High (Cert management) |
vsftpd if your client device strictly requires legacy FTP.
Hardware & Parts List for the FTP Status Monitor
Headless Pi servers often sit on a shelf without a monitor. To verify your FTP server is running and to display the Pi's local IP address for quick client connections, we will wire up an I2C OLED display and a GPIO status LED.
Required Components
- Board: Raspberry Pi 5 (8GB variant) - ~$80 USD
- Display: 0.96-inch SSD1306 I2C OLED (128x64, 3.3V logic) - ~$12 USD
- Indicator: 5mm Green LED + 330Ω current-limiting resistor
- Wiring: Female-to-female Dupont jumper wires
- Storage: 128GB+ microSD card (UHS-I Class 10 minimum for acceptable FTP write speeds)
Pin Mapping Table
The Raspberry Pi 5 maintains the standard 40-pin header layout. Wire the components exactly as specified below to match the Python code provided later.
| Component Pin | Pi 5 GPIO / Power | Physical Pin # | Function |
|---|---|---|---|
| OLED VCC | 5V Power | Pin 2 | Display Power |
| OLED GND | Ground | Pin 6 | Common Ground |
| OLED SDA | GPIO 2 (I2C1 SDA) | Pin 3 | I2C Data |
| OLED SCL | GPIO 3 (I2C1 SCL) | Pin 5 | I2C Clock |
| LED Anode (+) | GPIO 17 | Pin 11 | Status Output (via 330Ω) |
| LED Cathode (-) | Ground | Pin 9 | LED Ground |
Step-by-Step: Installing and Configuring vsftpd
The default vsftpd configuration is highly restrictive. We need to enable local user logins, write permissions, and chroot jails (which trap users in their home directory for security).
- Update and Install:
sudo apt update && sudo apt install vsftpd -y - Backup the Default Config:
sudo cp /etc/vsftpd.conf /etc/vsftpd.conf.bak - Edit the Configuration:
Open the file:sudo nano /etc/vsftpd.conf
Find and modify the following lines (uncomment them by removing the#if necessary):anonymous_enable=NO local_enable=YES write_enable=YES local_umask=022 chroot_local_user=YES allow_writeable_chroot=NO - The Chroot Writable Directory Workaround:
Modern vsftpd versions will block login if a chrooted user's home directory is writable (throwing a500 OOPSerror). The correct fix is to make the home directory read-only and create a writable subdirectory for uploads.sudo chmod a-w /home/pi sudo mkdir /home/pi/ftp_uploads sudo chown pi:pi /home/pi/ftp_uploads - Configure Passive Mode Ports:
Add these lines to the bottom ofvsftpd.confto define a narrow passive port range, making firewall rules easier:pasv_enable=YES pasv_min_port=30000 pasv_max_port=30100 - Restart the Service:
sudo systemctl restart vsftpd
For deeper configuration parameters, refer to the official vsftpd documentation or the Raspberry Pi Foundation networking guides.
Python FTP Watchdog & OLED Status Code
This Python script targets the Raspberry Pi 5 running Bookworm. It uses the luma.oled library to drive the SSD1306 display and gpiozero to control the status LED. It polls port 21 to verify the FTP daemon is listening and fetches the local IP address.
Prerequisites: sudo apt install python3-gpiozero i2c-tools and pip3 install luma.oled
import socket
import time
import subprocess
from gpiozero import LED
from luma.core.interface.serial import i2c
from luma.core.render import canvas
from luma.oled.device import ssd1306
# --- PIN DEFINITIONS ---
# GPIO 17 (Physical Pin 11) controls the green status LED
STATUS_LED = LED(17)
# I2C setup for SSD1306 (Address 0x3C is standard for most 0.96" modules)
try:
serial = i2c(port=1, address=0x3C)
device = ssd1306(serial, width=128, height=64)
display_available = True
except Exception as e:
print(f'OLED Init Failed: {e}')
display_available = False
def get_ip_address():
"""Fetches the primary LAN IP address of the Pi."""
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 80))
ip = s.getsockname()[0]
s.close()
return ip
except Exception:
return 'No Network'
def check_ftp_port(host='127.0.0.1', port=21):
"""Checks if vsftpd is actively listening on port 21."""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1.0)
result = sock.connect_ex((host, port))
sock.close()
return result == 0
except Exception:
return False
def main():
print('Starting FTP Watchdog...')
while True:
ftp_running = check_ftp_port()
ip_addr = get_ip_address()
# Update GPIO LED
if ftp_running:
STATUS_LED.on()
else:
STATUS_LED.blink(on_time=0.2, off_time=0.2)
# Update OLED Display
if display_available:
with canvas(device) as draw:
draw.text((0, 0), 'FTP Server Status', fill='white')
status_text = 'ONLINE' if ftp_running else 'OFFLINE'
draw.text((0, 16), f'State: {status_text}', fill='white')
draw.text((0, 32), f'Port: 21', fill='white')
draw.text((0, 48), f'IP: {ip_addr}', fill='white')
time.sleep(5)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
STATUS_LED.off()
if display_available:
device.cleanup()
print('Watchdog stopped.')
Troubleshooting: Exact Errors and Ranked Fixes
FTP is notoriously finicky regarding directory permissions and firewall states. If your client fails to connect, look for these exact error strings.
Error 1: 550 Permission denied.
This occurs when you attempt to upload a file or create a directory, but the server blocks the write operation.
- Cause 1 (Most Likely): You are trying to write directly to
/home/pi, which we made read-only in Step 4. Fix: Change your client's remote directory to/ftp_uploads. - Cause 2:
write_enable=YESis missing or commented out invsftpd.conf. Fix: Edit the config and restart the service. - Cause 3: The underlying filesystem is mounted read-only (common if the Pi experienced a hard crash and the SD card corrupted). Fix: Run
sudo fsck /dev/mmcblk0p2and reboot.
Error 2: ftp: connect: Connection timed out or Network unreachable
The client cannot establish the initial TCP handshake on port 21.
- Cause 1 (Most Likely): UFW (Uncomplicated Firewall) or iptables is blocking port 21. Fix: Run
sudo ufw allow 21/tcpandsudo ufw allow 30000:30100/tcp. - Cause 2: The
vsftpddaemon crashed or failed to start due to a syntax error in the config. Fix: Checksudo systemctl status vsftpd. - Cause 3: You are connecting from a different subnet and the Pi's default gateway is missing. Fix: Verify routing with
ip route.
1. Run
sudo systemctl status vsftpd to ensure the daemon is active (green).2. Run
sudo ufw status to verify ports 21 and 30000:30100 are marked ALLOW.3. Run
ls -ld /home/pi to confirm the directory permissions are dr-xr-xr-x (not writable by the user).
Extending and Simplifying the Build
How to Simplify: Drop FTP for SFTP
If you control the client devices, uninstall vsftpd (sudo apt purge vsftpd) and use SFTP. SFTP uses the existing SSH daemon on port 22. It requires no extra configuration, no passive port forwarding, and encrypts both the authentication and the data payload. You can map an SFTP drive in Windows using tools like Mountain Duck or SSHFS-Win, making the Pi appear as a native network drive.
How to Extend: USB 3.0 NAS and FTPS
The Raspberry Pi 5's PCIe 2.0 interface and USB 3.0 ports allow for much higher throughput than the microSD card's ~40MB/s ceiling. To extend this build into a true NAS:
- Attach a USB 3.0 to NVMe enclosure with a 1TB SSD.
- Format it as
ext4and mount it to/mnt/nas_drivevia/etc/fstab. - Create a dedicated FTP user whose home directory is mapped to the SSD mount point.
- Upgrade to FTPS by generating a self-signed certificate (
openssl req -x509 -nodes -days 365 -newkey rsa:2048) and pointingrsa_cert_fileinvsftpd.confto the generated.pemfile. This encrypts the session while maintaining compatibility with legacy FTP clients that support AUTH TLS.






