I run my servers inside Proxmox, so in this case the FTP service lives on an Ubuntu Server VM there. The same steps apply whether you’re on bare metal or any other hypervisor.
Create the VM
Install Ubuntu Server LTS and give it a static LAN IP so it’s easy to reach later. 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, double-check the network adapter mode and make sure the IP address is in the right subnet.
Update the OS
Bring the system up to date before adding anything else.
sudo apt update && sudo apt upgrade -y
Mirror issues can cause failures. Running the command again usually clears it up.
Install vsftpd
Install the FTP daemon package.
sudo apt install vsftpd -y
If the service doesn’t appear to exist, confirm with systemctl status vsftpd
.
Create an FTP User
I added a dedicated account just for FTP traffic so it’s separate from system logins.
sudo adduser ftpuser
Set a password when prompted.
Later login failures (530 Login incorrect
) often mean the account is disabled in /etc/ftpusers
or local_enable
isn’t set in the config.
Set Up the Directory Structure
vsftpd
enforces that a chroot directory can’t be writable. To work around this, I made 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 chroot
usually means permissions on/srv/ftp
are wrong. It must be owned by root and not writable.
Configure vsftpd
Open the config:
sudo nano /etc/vsftpd.conf
Here’s what 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 isn’t open.
- If the server advertises
127.0.1.1
in passive mode, addpasv_address=<your_LAN_IP>
to the config.
Restart and Enable the Service
Restart the daemon and enable it at boot.
sudo systemctl restart vsftpd
sudo systemctl enable vsftpd
If it fails to enable, check the logs:
journalctl -u vsftpd -n 50 --no-pager
Adjust the Firewall
If UFW is enabled, open the command port and the passive range.
sudo ufw allow 21/tcp
sudo ufw allow 30000:30049/tcp
UFW must be active for these rules to matter. Verify with sudo ufw status
.
Test Locally
Before trying 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 config.550 Permission denied
: verify/srv/ftp/uploads
is 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 gone overboard with hardening them for internet exposure. Network security isn’t my specialty, so I stick to the basics. If you’ve got suggestions for tightening things up, either for FTP specifically or my home lab in general, I’d be glad to hear them.