Pure-FTPd Secure FTPS Server on AWS User Guide
Overview
This guide covers the deployment and configuration of Pure-FTPd on AWS using cloudimg AWS Marketplace AMIs. Pure-FTPd is a small, fast, security focused FTP server, and this image runs it in an FTPS first posture: explicit TLS is required on both the control channel and the data channel, so credentials and file contents are never sent in the clear.
The image installs Pure-FTPd from the official Ubuntu 24.04 apt archive, run under systemd as pure-ftpd.service. The server listens for the FTP control channel on tcp port 21 and uses a passive data port range of 30000 to 30009. There is no web interface: the configuration under /etc/pure-ftpd/conf is the whole configuration, and logins are served from a compact PureDB virtual-user database mapped to a single unprivileged system account.
Secure by default, no known bootstrap credential. The stock package ships no usable login in this image. On the first boot of every instance a one shot service creates a unique virtual FTP user with a strong random password, generates a unique self signed TLS certificate for that specific instance, sets the passive mode address (ForcePassiveIP) to the instance address, then writes the user, password and host to a root owned credentials file. Every user is chrooted to its own home directory, anonymous access is refused, and only the PureDB virtual user can authenticate, so no two instances share a credential and no real system account is ever reachable over FTP.
What is included:
-
Pure-FTPd from the Ubuntu 24.04 apt archive, run under systemd as
pure-ftpd.service -
FTPS (explicit TLS) required on the control and data channels (
TLS 2), so a plaintext FTP login is refused -
A per instance unique virtual FTP user and password and a unique self signed TLS certificate, generated on first boot and written to
/root/pure-ftpd-credentials.txt(mode 0600) -
Every virtual user chrooted to its own home directory under
/srv/ftp, so an account sees only its own files -
No anonymous access (
NoAnonymous yes), and PureDB only authentication (UnixAuthentication no), so no real system account is exposed over FTP -
A defined passive port range (30000 to 30009) and a ForcePassiveIP value set to this instance's address for passive mode behind network address translation
-
The file store on a dedicated data volume mounted at
/srv/ftp, plus the helper toolspureftpd-adduserandpureftpd-listusers
Connecting to your instance
Connect over SSH using your EC2 key pair, as the default login user for the AMI variant you launched:
| AMI variant | SSH login user |
|---|---|
| Pure-FTPd on Ubuntu 24.04 | ubuntu |
For example: ssh -i your-key.pem ubuntu@YOUR_INSTANCE_IP. The administrator account uses SSH key authentication with the EC2 key pair you selected at launch.
Prerequisites
- An EC2 instance launched from this cloudimg Pure-FTPd AMI.
- An EC2 Security Group that allows inbound tcp 21 (FTP control) and tcp 30000 to 30009 (FTPS passive data), scoped to your client addresses only. Do not expose FTP to the whole internet.
- SSH access to the instance as
ubuntu.
FTPS uses one connection for control and a separate connection for each transfer, so both port 21 and the passive range must be open to your clients. The passive range is small by design; widen it in /etc/pure-ftpd/conf/PassivePortRange and the Security Group if you expect many concurrent transfers, then restart the service.
Verify the FTPS server is running
Connect over SSH as ubuntu, then confirm pure-ftpd.service is active, check the installed version, and confirm the server is listening on port 21 with the file store on its own volume.
sudo systemctl is-active pure-ftpd
pure-ftpd --help 2>&1 | head -1
sudo ss -tlnH | grep ':21 '
You should see active, the Pure-FTPd version banner, and a listener bound to *:21 for the FTP control channel.

Retrieve the per instance credentials and test a secure transfer
On the first boot of your instance, Pure-FTPd generated a unique virtual FTP user, a unique password and a unique TLS certificate. The user, password and host are stored in the root owned credentials file, which contains ftp.user, ftp.password and ftp.host, each unique to this instance:
sudo cat /root/pure-ftpd-credentials.txt
Test a secure transfer end to end with lftp, which is preinstalled: the block below reads the generated user and password straight from that file (without printing the password), forces explicit TLS, uploads a file, downloads it back, and confirms the contents match. Passive mode is turned off for this loopback self test; real clients use passive mode over the 30000 to 30009 range.
U=$(sudo awk -F= '/^ftp\.user=/{print $2; exit}' /root/pure-ftpd-credentials.txt)
P=$(sudo awk -F= '/^ftp\.password=/{print $2; exit}' /root/pure-ftpd-credentials.txt)
rm -f /tmp/guide-up.txt /tmp/guide-dl.txt
echo "cloudimg FTPS round trip $(date -u '+%Y-%m-%d %H:%M UTC')" > /tmp/guide-up.txt
lftp -u "$U","$P" -e 'set xfer:clobber on; set ssl:verify-certificate no; set ftp:ssl-force true; set ftp:ssl-protect-data true; set ftp:passive-mode false; put /tmp/guide-up.txt -o report.txt; ls; get report.txt -o /tmp/guide-dl.txt; rm report.txt; bye' 127.0.0.1
cmp -s /tmp/guide-up.txt /tmp/guide-dl.txt && echo "FTPS round trip OK: download is byte-identical to the upload over required TLS"
The upload and download both succeed over explicit TLS and the downloaded contents match what you uploaded, proving the FTPS pipeline (TLS negotiation, the per instance user and password, and the chrooted home directory) works end to end.

Confirm plaintext and anonymous access are refused
A secure FTP server must refuse anything less than TLS. This image requires explicit TLS, refuses anonymous logins, and chroots each user to its home directory. The block below tests the first two: a plaintext (no TLS) login and an anonymous login each fail.
U=$(sudo awk -F= '/^ftp\.user=/{print $2; exit}' /root/pure-ftpd-credentials.txt)
P=$(sudo awk -F= '/^ftp\.password=/{print $2; exit}' /root/pure-ftpd-credentials.txt)
PT=$(lftp -u "$U","$P" -e 'set ftp:ssl-force false; set ftp:ssl-allow false; set net:timeout 8; set net:max-retries 1; ls; bye' 127.0.0.1 2>&1 || true)
case "$PT" in *530*|*"Login failed"*) echo "plaintext FTP login refused (explicit TLS is required)";; *) echo "unexpected: $PT";; esac
AN=$(lftp -u anonymous,anon@example.com -e 'set ssl:verify-certificate no; set ftp:ssl-force true; set net:timeout 8; set net:max-retries 1; ls; bye' 127.0.0.1 2>&1 || true)
case "$AN" in *530*|*"Login failed"*|*"authentication failed"*) echo "anonymous login refused (NoAnonymous is on)";; *) echo "unexpected: $AN";; esac
Each attempt is rejected: the plaintext login is refused because explicit TLS is required, and the anonymous login is refused because anonymous access is disabled. Because every virtual user is chrooted to its own home under /srv/ftp, an account can never browse the rest of the server.

Review the hardening configuration
All of the hardening lives in the Pure-FTPd conf directory as one file per directive, applied by pure-ftpd-wrapper:
cd /etc/pure-ftpd/conf
grep -H '' NoAnonymous TLS ChrootEveryone UnixAuthentication PassivePortRange PureDB
The configuration enforces NoAnonymous yes (no anonymous FTP), TLS 2 (explicit FTPS required on control and data), ChrootEveryone yes (jail every user to its home), UnixAuthentication no (PureDB virtual users only, never real system accounts), and PassivePortRange 30000 30009. The per instance passive address is written to /etc/pure-ftpd/conf/ForcePassiveIP on first boot. Edit any file and restart with sudo systemctl restart pure-ftpd after changes.

Connect from an FTPS client
From your own workstation, connect with any FTPS capable client using explicit TLS (also called FTP over TLS, or AUTH TLS). Use the ftp.user, ftp.password and ftp.host values from the credentials file, port 21, and enable explicit TLS with passive mode. Because the certificate is self signed, accept it on first connection or replace it with your own certificate. For example, with lftp or curl from your workstation:
# Replace the host and password with the values from /root/pure-ftpd-credentials.txt.
# lftp (explicit FTPS, passive mode over the 30000-30009 range):
lftp -u cloudimg,YOUR_PASSWORD -e 'set ftp:ssl-force true; set ftp:ssl-protect-data true; set ssl:verify-certificate no; put report.pdf; ls; bye' YOUR_INSTANCE_IP
# curl (explicit FTPS):
curl --ssl-reqd --insecure -u cloudimg:YOUR_PASSWORD -T report.pdf ftp://YOUR_INSTANCE_IP/report.pdf
Graphical clients such as FileZilla and WinSCP work the same way: choose the protocol FTP with Require explicit FTP over TLS, set the host to your instance address, the port to 21, and passive mode. Ensure your EC2 Security Group allows inbound tcp 21 and tcp 30000 to 30009 from your client address.
Add more users and replace the certificate
Each FTP account is a PureDB virtual user chrooted to its own home under /srv/ftp, all backed by one unprivileged system account. To add another isolated FTP account, use the helper tool, which generates a strong password and creates the chrooted home:
# Create a new virtual FTP user with a generated password:
sudo pureftpd-adduser alice
# List the virtual FTP users:
sudo pureftpd-listusers
To use a certificate signed by a certificate authority instead of the per instance self signed one, replace /etc/ssl/private/pure-ftpd.pem (concatenated key and certificate) and restart the service. For advanced use the standard pure-pw tool remains available for per user quotas, bandwidth limits, and virtual user management within the PureDB database.
Support
cloudimg provides 24/7 technical support for this Pure-FTPd image by email (support@cloudimg.co.uk) and live chat. We help with connecting FTPS clients, managing virtual users and the PureDB backend, configuring chroot and quotas, opening the control port and passive range in your Security Group, setting the passive mode address behind network address translation, TLS certificate management, version upgrades and troubleshooting. Critical issues receive a one hour average response time.
All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.