On this page 27 sections
  1. What vsftpd is
  2. Install and start the service
  3. Main configuration file
  4. Listen configuration
  5. Enable local users
  6. Important: allow local users to upload with put
  7. Prepare a writable upload directory
  8. Test put as a local user
  9. Local umask
  10. Chroot local users
  11. Writable chroot compatibility option
  12. Set a common local root
  13. Per-user configuration
  14. Restrict which local users may connect
  15. Passive FTP
  16. Firewall rules
  17. Anonymous read-only access
  18. Anonymous uploads
  19. Logging
  20. Connection limits and timeouts
  21. Enable TLS for FTPS
  22. Complete local-user configuration
  23. Apply and verify changes
  24. Troubleshoot failed uploads
  25. Troubleshoot hanging listings or transfers
  26. FTP client quick reference
  27. FTP, FTPS and SFTP
01

What vsftpd is

vsftpd, or Very Secure FTP Daemon, is an FTP server for Unix-like systems. FTP normally uses TCP port 21 for its control connection and a separate data connection for directory listings and file transfers.

Traditional FTP does not encrypt credentials or transferred data. Use FTPS when FTP compatibility is required over an untrusted network, or consider SFTP when SSH-based file transfer is suitable.

02

Install and start the service

On Debian and Ubuntu, install vsftpd with APT and enable the service at boot.

Install vsftpd
sudo apt update
sudo apt install vsftpd
Enable and start the service
sudo systemctl enable --now vsftpd
Check service status
sudo systemctl --no-pager --full status vsftpd
03

Main configuration file

The main configuration file is normally /etc/vsftpd.conf. Back it up before making changes.

Back up and edit the configuration
sudo cp /etc/vsftpd.conf /etc/vsftpd.conf.bak
sudo nano /etc/vsftpd.conf
Show active directives only
grep -Ev '^[[:space:]]*(#|$)' /etc/vsftpd.conf
04

Listen configuration

Changing the port does not encrypt or secure FTP. It only changes where the service listens.

IPv4 listener configuration
listen=YES
listen_ipv6=NO
listen_port=21
05

Enable local users

local_enable=YES allows local Linux accounts to authenticate. Anonymous access can remain disabled when the server is intended only for known users.

Enable local accounts
anonymous_enable=NO
local_enable=YES
Create a local FTP user
sudo adduser ftpuser
06

Important: allow local users to upload with put

anon_upload_enable is only for anonymous users. It is not required for a local authenticated user to run put.

Minimum local-user upload settings
local_enable=YES
write_enable=YES
local_umask=022
07

Prepare a writable upload directory

The user needs execute permission on every parent directory and write plus execute permission on the destination directory.

Create a private upload directory
sudo install -d -o ftpuser -g ftpuser -m 0750 /home/ftpuser/uploads
Inspect every component of the path
namei -l /home/ftpuser/uploads
08

Test put as a local user

Avoid placing real passwords directly in a command when shell history or process visibility is a concern. curl -u ftpuser prompts for the password.

Create a test file
echo 'vsftpd upload test' > test.txt
Connect with the FTP client
ftp SERVER_IP
Upload from the FTP prompt
cd uploads
put test.txt
ls
Test an upload with curl
curl -u ftpuser -T test.txt ftp://SERVER_IP/uploads/
09

Local umask

Use 002 instead when files should remain group-writable in a controlled shared group.

Common local umask
local_umask=022
Group-writable local umask
local_umask=002
10

Chroot local users

The safer layout is a non-writable chroot root containing a writable subdirectory. This avoids making the root of the jail writable.

Chroot every local user
chroot_local_user=YES
Create a safer chroot layout
sudo chown root:root /home/ftpuser
sudo chmod 0755 /home/ftpuser
sudo install -d -o ftpuser -g ftpuser -m 0750 /home/ftpuser/uploads
11

Writable chroot compatibility option

This option does not enable uploads by itself. write_enable=YES and suitable filesystem permissions are still required.

Allow a writable chroot root
chroot_local_user=YES
allow_writeable_chroot=YES
12

Set a common local root

If several users share this path, manage ownership and group permissions carefully instead of granting broad world-write permissions.

Configure a local FTP root
local_root=/srv/ftp/users
13

Per-user configuration

user_config_dir allows individual users to override supported directives. Each file inside the directory must have the same name as the Linux account.

Enable per-user configuration
user_config_dir=/etc/vsftpd/user_config
Give ftpuser a specific root
sudo mkdir -p /etc/vsftpd/user_config
echo 'local_root=/srv/ftp/ftpuser' | sudo tee /etc/vsftpd/user_config/ftpuser
14

Restrict which local users may connect

With userlist_deny=NO, the meaning changes: only users in the file may log in.

Configure a deny list
userlist_enable=YES
userlist_deny=YES
userlist_file=/etc/vsftpd.user_list
Block one user
echo 'blockeduser' | sudo tee -a /etc/vsftpd.user_list
Configure an allow list
userlist_enable=YES
userlist_deny=NO
userlist_file=/etc/vsftpd.allowed_users
15

Passive FTP

When the server is behind NAT, advertise the public address or DNS name that clients can reach.

Configure passive ports
pasv_enable=YES
pasv_min_port=40000
pasv_max_port=40100
Advertise a public address
pasv_address=ftp.example.com
pasv_addr_resolve=YES
16

Firewall rules

The router or cloud firewall must also forward or permit the same ports when applicable.

Allow FTP through UFW
sudo ufw allow 21/tcp
sudo ufw allow 40000:40100/tcp
sudo ufw status
17

Anonymous read-only access

Anonymous FTP can provide public downloads. Keep write access disabled unless there is a clear operational requirement.

Anonymous download configuration
anonymous_enable=YES
local_enable=NO
anon_root=/srv/ftp/public
write_enable=NO
no_anon_password=YES
Prepare the public directory
sudo install -d -o root -g root -m 0755 /srv/ftp/public
18

Anonymous uploads

Exact service account and group names vary by distribution. Verify them before applying ownership changes.

Anonymous upload directives
anonymous_enable=YES
anon_root=/srv/ftp/anonymous
write_enable=YES
anon_upload_enable=YES
anon_mkdir_write_enable=YES
anon_umask=022
Create a dedicated anonymous upload directory
sudo install -d -o root -g root -m 0755 /srv/ftp/anonymous
sudo install -d -o ftp -g nogroup -m 0730 /srv/ftp/anonymous/uploads
19

Logging

Enable transfer logging for operational troubleshooting and security review. Protocol logging is more verbose and may record sensitive session details, so use it deliberately.

Enable transfer and protocol logging
xferlog_enable=YES
log_ftp_protocol=YES
vsftpd_log_file=/var/log/vsftpd.log
Review recent service logs
sudo journalctl -u vsftpd -n 50 --no-pager
sudo tail -n 50 /var/log/vsftpd.log
20

Connection limits and timeouts

Limits can reduce resource exhaustion and abandoned sessions. Choose values that fit the expected number of legitimate users.

Configure basic limits
max_clients=50
max_per_ip=5
idle_session_timeout=600
data_connection_timeout=120
delay_failed_login=3
21

Enable TLS for FTPS

Use a certificate trusted by clients in production. Available TLS directives can vary by distribution and vsftpd build, so confirm them with the installed manual.

Create a self-signed lab certificate
sudo openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
  -keyout /etc/ssl/private/vsftpd.key \
  -out /etc/ssl/certs/vsftpd.crt
sudo chmod 0600 /etc/ssl/private/vsftpd.key
Require TLS for local users
ssl_enable=YES
allow_anon_ssl=NO
force_local_logins_ssl=YES
force_local_data_ssl=YES
rsa_cert_file=/etc/ssl/certs/vsftpd.crt
rsa_private_key_file=/etc/ssl/private/vsftpd.key
ssl_sslv2=NO
ssl_sslv3=NO
22

Complete local-user configuration

Create a writable subdirectory such as /home/ftpuser/uploads; do not assume this configuration alone grants filesystem access.

Local-user FTP baseline
listen=YES
listen_ipv6=NO

anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022

chroot_local_user=YES

pasv_enable=YES
pasv_min_port=40000
pasv_max_port=40100

xferlog_enable=YES
log_ftp_protocol=YES

idle_session_timeout=600
data_connection_timeout=120
23

Apply and verify changes

Restart vsftpd, check that it started successfully and confirm that the expected socket is listening.

Restart and validate the service
sudo systemctl restart vsftpd
sudo systemctl --no-pager --full status vsftpd
sudo ss -lntp | grep ':21'
Inspect errors after a failed restart
sudo journalctl -u vsftpd -n 50 --no-pager
24

Troubleshoot failed uploads

Common causes include:

  • write_enable=NO or a missing write_enable directive.
  • The destination directory is not writable by the authenticated Linux account.
  • A parent directory lacks execute permission.
  • The FTP user is chrooted somewhere different from the expected path.
  • Mandatory access controls such as AppArmor or SELinux deny the operation.
  • The filesystem is mounted read-only or has no free space.
Check the required local upload directives
grep -E '^(local_enable|write_enable|local_root|local_umask|chroot_local_user|allow_writeable_chroot)=' /etc/vsftpd.conf
Check the destination directory
namei -l /home/ftpuser/uploads
sudo -u ftpuser test -w /home/ftpuser/uploads && echo writable || echo not-writable
25

Troubleshoot hanging listings or transfers

The passive port range must match across vsftpd, the host firewall, any cloud firewall and NAT port forwarding.

Review passive settings and firewall state
grep -E '^pasv_' /etc/vsftpd.conf
sudo ufw status
sudo ss -lntp | grep ':21'
26

FTP client quick reference

These commands run inside an interactive FTP client.

Common FTP client commands
ls
pwd
cd remote-directory
lcd local-directory
get file.txt
put file.txt
mget *.txt
mput *.txt
binary
passive
delete file.txt
mkdir directory
rmdir directory
bye
27

FTP, FTPS and SFTP

  • FTP is the traditional protocol, normally using TCP 21, without encryption by default.
  • FTPS is FTP protected by TLS and can be served by vsftpd.
  • SFTP is the SSH File Transfer Protocol, normally using TCP 22.

SFTP is not FTP over TLS and is not provided by vsftpd.