I run my servers inside Proxmox, so the FTP service is set up on an Ubuntu Server VM. The same steps apply whether you’re using bare metal or another hypervisor.
Create the VM
Install Ubuntu Server LTS and assign it a static LAN IP for easier access. I used a bridged adapter in Proxmox so the VM behaves like any other machine on my network.
If the VM can’t be reached from another machine, check the network adapter mode and verify the IP address is in the correct subnet.
Update the OS
Update the system before installing additional software.
sudo apt update && sudo apt upgrade -y
Install vsftpd
Install the FTP daemon package.
sudo apt install vsftpd -y
If the service doesn’t appear to be running, check its status with systemctl status vsftpd.
Create an FTP User
I created a dedicated account for FTP traffic to keep it separate from system logins.
sudo adduser ftpuser
Set a password when prompted.
Login failures (530 Login incorrect) often indicate the account is disabled in /etc/ftpusers or local_enable isn’t set in the configuration.
Set Up the Directory Structure
vsftpd requires that a chroot directory cannot be writable. To work around this, I created a locked root folder with a writable subdirectory for uploads.
sudo mkdir -p /srv/ftp/uploads
sudo chown root:root /srv/ftp
sudo chmod 755 /srv/ftp
sudo chown ftpuser:ftpuser /srv/ftp/uploads
Common errors:
500 OOPS: vsftpd: refusing to run with writable root inside chrootusually means permissions on/srv/ftpare incorrect. It must be owned by root and not writable.
Configure vsftpd
Open the configuration file:
sudo nano /etc/vsftpd.conf
Here’s the configuration I used:
listen=YES
listen_ipv6=NO
anonymous_enable=NO
local_enable=YES
write_enable=YES
chroot_local_user=YES
- If directory listings stall, the passive port range may not be open.
- If the server advertises
127.0.1.1in passive mode, addpasv_address=<your_LAN_IP>to the configuration.
Restart and Enable the Service
Restart the daemon and enable it to start at boot.
sudo systemctl restart vsftpd
sudo systemctl enable vsftpd
If it fails to start, check the logs:
journalctl -u vsftpd -n 50 --no-pager
Adjust the Firewall
If UFW is enabled, open the command port and the passive port range.
sudo ufw allow 21/tcp
sudo ufw allow 30000:30049/tcp
UFW must be active for these rules to take effect. Verify with sudo ufw status.
Test Locally
Before testing with a client, I tested from the server itself.
curl -v --user 'ftpuser:YourPassword' ftp://127.0.0.1/
curl -v --user 'ftpuser:YourPassword' -T /etc/hosts ftp://127.0.0.1/uploads/
ls -l /srv/ftp/uploads
530 Login incorrect: check username, password, and configuration.550 Permission denied: verify/srv/ftp/uploadsis owned byftpuser.
Connect from Another Machine
Connect with an FTP client using your VM’s IP address:
- Host:
<LAN_IP> - Port: 21
- Protocol: FTP
- Username:
ftpuser - Password: your password
- Transfer mode: Passive
I only use these FTP servers on my local network, so I haven’t implemented extensive hardening for internet exposure. Network security isn’t my specialty, so I stick to basic configurations. If you have suggestions for improving security, either for FTP specifically or my home lab in general, I’d appreciate hearing them.