Skip to content
Products
AI Website Builder New VPS Hosting Cloud Servers Web Hosting Dedicated Servers Domains
Company
About Documentation Support Center Contact Get Started Call +91 75795 45488
Login
Hosting Panel — cPanel & Billing Console Panel — VPS Management
ALL SYSTEMS OPERATIONAL
VPS

Set Up an FTP Server on a VPS with vsftpd (TLS + Chroot)

Getwebup 6 min read

If you moved from cPanel shared hosting to a bare VPS, you probably noticed FTP just isn't there anymore. No File Manager, no "FTP Accounts" tab — just a blank server and whatever you install yourself. If a legacy tool, a client's desktop app, or an old deployment script still needs FTP (not SFTP), you'll have to stand up the server yourself. Here's how to do it with vsftpd without leaving an open door on your VPS.

Why this is different from cPanel FTP

On shared hosting, cPanel creates the FTP account, jails it to the right directory, and opens the ports for you. On a VPS, none of that exists until you build it. That means three things you're now responsible for: who the FTP user actually is on the Linux system, how tightly they're locked into their own directory, and which ports your firewall (and your cloud provider's security group, if you're on AWS/DigitalOcean/etc.) needs to allow.

Get any one of those wrong and you either end up with a server that refuses to start, or worse, an FTP user who can wander outside their folder.

Before you start

  • Root or sudo access to the VPS (Ubuntu/Debian or AlmaLinux/RHEL steps are both below)
  • A domain or static IP pointing at the server, if clients will connect by hostname
  • Access to your firewall rules — both the OS firewall (ufw/firewalld) and any cloud security group
  • A decision up front: do you actually need FTP, or would SFTP (which rides on SSH, port 22, and needs none of this) do the job? If you control the client, use SFTP. If a legacy app forces plain FTP, keep reading.

Step 1: Install vsftpd

vsftpd ("very secure FTP daemon") is the standard choice on Linux — small, fast, and it doesn't run as root once it's serving connections.

# Ubuntu / Debian
sudo apt update
sudo apt install vsftpd -y

# AlmaLinux / Rocky / CentOS
sudo dnf install vsftpd -y
sudo systemctl enable --now vsftpd

Back up the default config before touching anything — you'll want it if you need to compare later:

sudo cp /etc/vsftpd.conf /etc/vsftpd.conf.bak

Step 2: Create a dedicated FTP user

Don't hand out the root account or an existing sudo user for FTP. Create one scoped to the folder they should live in.

sudo adduser ftpuser
sudo mkdir -p /home/ftpuser/uploads
sudo chown nobody:nogroup /home/ftpuser
sudo chmod a-w /home/ftpuser
sudo chown ftpuser:ftpuser /home/ftpuser/uploads

That ownership split matters more than it looks. vsftpd's chroot feature refuses to jail a user into a directory that user can write to — it's a security check baked into the daemon, not a bug. The home directory (/home/ftpuser) has to be owned by someone else and non-writable; the actual working folder (uploads) is where the FTP user gets write access.

Step 3: Lock the config down

Open /etc/vsftpd.conf and set these explicitly — most distros ship with some of these commented out or on defaults you don't want:

DirectiveValueWhy
anonymous_enableNONo anonymous uploads/downloads, ever
local_enableYESAllow local system users to log in
write_enableYESAllow uploads (read-only server? set NO)
chroot_local_userYESJail every user to their own home directory
allow_writeable_chrootNOEnforce the non-writable-home rule from Step 2
user_sub_token$USERUsed with local_root below for per-user folders
local_root/home/$USER/uploadsLand users straight in their upload folder
pasv_enableYESNeeded for almost every modern FTP client

Restart to apply, then check the logs before moving on:

sudo systemctl restart vsftpd
sudo tail -f /var/log/vsftpd.log

Step 4: Add TLS — plain FTP sends passwords in cleartext

By default, FTP logins go over the wire unencrypted. Anyone on the same network path can read the username and password in plain text. Fix it with a self-signed cert (fine for internal/legacy tools) or a real one if clients will complain about warnings.

sudo mkdir -p /etc/vsftpd
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048   -keyout /etc/vsftpd/vsftpd.pem -out /etc/vsftpd/vsftpd.pem

Then add to /etc/vsftpd.conf:

rsa_cert_file=/etc/vsftpd/vsftpd.pem
rsa_private_key_file=/etc/vsftpd/vsftpd.pem
ssl_enable=YES
force_local_data_ssl=YES
force_local_logins_ssl=YES
ssl_tlsv1_2=YES
ssl_sslv2=NO
ssl_sslv3=NO

This turns your server into FTPS (FTP over TLS) — same port 21, but the client has to explicitly request "explicit FTP over TLS" in its connection settings, or the login will just hang.

Step 5: Fix passive mode and the firewall (the part everyone gets stuck on)

This is where 90% of "it connects but then times out on directory listing" reports come from. FTP in passive mode opens a second, random high port for data transfer — and if your firewall only allows port 21, that second connection dies silently.

Pin the passive port range in /etc/vsftpd.conf instead of leaving it random:

pasv_enable=YES
pasv_min_port=40000
pasv_max_port=40100
pasv_address=YOUR_SERVER_PUBLIC_IP

pasv_address matters if your VPS sits behind NAT or a cloud load balancer — without it, vsftpd hands the client its private internal IP for the data connection, which the client obviously can't reach.

Now open exactly those ports, plus 21, in both the OS firewall and your cloud provider's security group (they're separate — missing either one breaks it the same way):

# ufw (Ubuntu/Debian)
sudo ufw allow 21/tcp
sudo ufw allow 40000:40100/tcp

# firewalld (AlmaLinux/RHEL)
sudo firewall-cmd --permanent --add-port=21/tcp
sudo firewall-cmd --permanent --add-port=40000-40100/tcp
sudo firewall-cmd --reload

Restart vsftpd once more, then test from a client with FTPS (explicit TLS) enabled and passive mode on.

Common errors and what actually causes them

ErrorReal cause
500 OOPS: vsftpd: refusing to run with writable root inside chrootThe user's home directory is writable by that same user. Fix ownership per Step 2 — vsftpd won't start the session otherwise.
Connects, login OK, then hangs on file listingPassive port range not open in the firewall or cloud security group. See Step 5.
530 Login incorrectEither wrong credentials, or the user's shell is set to /usr/sbin/nologin and isn't in /etc/shells — some vsftpd builds check this even for FTP-only accounts.
Client shows "AUTH TLS failed" or a certificate warningClient isn't set to explicit FTPS, or it's rejecting the self-signed cert — either switch to explicit TLS in the client or install a real certificate.

Prevention: keep this server from becoming your weak point

  • Add the FTP port to your fail2ban jails — brute-force login attempts against FTP are constant and automated.
  • Never reuse an existing sudo/admin Linux account for FTP; a compromised FTP password shouldn't hand over shell access.
  • Rotate the passive port range and firewall rules if you ever open the server up more broadly (e.g. adding a second FTP user for a different client).
  • Check /var/log/vsftpd.log periodically, or ship it to your monitoring tool — repeated failed logins from one IP is your early warning.
  • If you can migrate the workflow to SFTP later, do it — it uses your existing SSH hardening (keys, fail2ban, no separate cert management) instead of a whole parallel stack.

Frequently asked questions

Do I need vsftpd if my VPS already has SSH access?

Not necessarily. SSH access already gives you SFTP, which is encrypted by default and needs no extra ports or certificates. Only set up vsftpd if a specific tool or client can't be switched to SFTP.

Why does vsftpd refuse to start with a "writable root inside chroot" error?

vsftpd checks that a chrooted user's home directory isn't writable by that same user, since a writable chroot root can be used to escape the jail. Make the home directory owned by root or nobody and non-writable, then give the user write access to a subfolder inside it instead.

Can I run vsftpd and SFTP on the same VPS at the same time?

Yes, they don't conflict — vsftpd listens on port 21 (or 990 for implicit FTPS) and SFTP rides on port 22 through SSH. Just make sure both sets of firewall rules are correct.

My FTP client connects but the file listing never loads. What's wrong?

Almost always a passive-mode port problem. Pin pasv_min_port and pasv_max_port in vsftpd.conf, then open that exact range in both your OS firewall and your cloud provider's security group.

Is a self-signed certificate good enough for FTPS?

It's fine for internal tools or a client you control, since the connection is still encrypted. For third-party clients who'll see certificate warnings, use a free Let's Encrypt certificate instead — the setup in vsftpd.conf is identical, just point rsa_cert_file at the Let's Encrypt fullchain and key.

#vsftpd #ftp-server #vps #tls #chroot #linux

Keep reading

Chat with Support