Set up secure SFTP access

2 min read07 November 2026

SFTP runs over SSH and is a better choice than FTP, which sends credentials and files in clear text. For a contractor or colleague who needs to drop files, create a dedicated account, without a shell, confined to a specific folder. The main pitfall is the chroot: sshd refuses the connection when permissions are not exact.

Create a dedicated account

Bash
sudo groupadd sftpusers
sudo adduser --shell /usr/sbin/nologin --ingroup sftpusers user

nologin blocks the interactive shell but not SFTP. The user has no sudo access. If you prefer a key, install it in /home/user/.ssh/authorized_keys before disabling the password.

Configure sshd

Open /etc/ssh/sshd_config with sudoedit and add this block at the end:

Configuration
Match Group sftpusers
    ChrootDirectory /srv/sftp/%u
    ForceCommand internal-sftp
    AllowTcpForwarding no
    X11Forwarding no
    PermitTunnel no

The Match block must be at the end of the configuration. On Debian, files in /etc/ssh/sshd_config.d/ are included at the top of the main file: a Match placed there would make the following directives conditional. ChrootDirectory confines the user to their folder; ForceCommand internal-sftp ignores any shell command. %u is replaced by the account name. Validate before reloading:

Bash
sudo sshd -t
sudo systemctl reload ssh

On Debian, the service is called ssh. Keep a root session open during the test: a configuration error can cut SSH off.

Chroot permissions

The chroot directory and all its parents must be owned by root and must not be writable by the user, otherwise sshd refuses the connection.

Bash
sudo mkdir -p /srv/sftp/user/uploads
sudo chown root:root /srv/sftp /srv/sftp/user
sudo chmod 755 /srv/sftp /srv/sftp/user
sudo chown user:sftpusers /srv/sftp/user/uploads
sudo chmod 750 /srv/sftp/user/uploads

The user writes only in uploads. 777 permissions would break the separation and expose the files. Do not store a private key or sensitive configuration in this folder.

Test

Bash
sftp [email protected]
pwd
put file.txt
ls -l

pwd should show /. Try cd ..: the result must stay inside the chroot. Check journalctl -u ssh -n 50 on Broken pipe or an immediate close. Permission denied usually comes from the shell, password or chroot rights. Write failed almost always means a parent directory writable by the user.

To publish to a website, sync from uploads to /var/www with an rsync script run by a deployment account. Do not give the SFTP user direct access to the web root: they could replace code executed by PHP.

Advertisement

Was this article helpful?

Read next