Published: • 4 min read

Setting Up an FTP Server on Ubuntu with vsftpd

Table of Contents

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, add pasv_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 by ftpuser.

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.