How to Create SFTP-Only Users on Linux (File Access Without a Shell)

SFTP-only users: a folder, not a shell — For clients, cameras and relatives.

Sometimes someone needs to put files on your server and nothing more: a photographer's client collecting a shoot, a security camera dumping recordings, a relative dropping documents for you to print. Giving them a normal account hands them a shell and a view of the whole system. An SFTP-only user is locked into one folder, can't run commands, and still works with WinSCP, FileZilla and every phone file app.

Quick reference

StepWhat
Groupsudo groupadd sftponly
User with no shellsudo useradd -m -g sftponly -s /usr/sbin/nologin client1
Jail folder/srv/sftp/client1 owned by root, 755
Writable subfolder/srv/sftp/client1/upload owned by the user
sshdMatch Group sftponly block with ChrootDirectory and ForceCommand internal-sftp
Testsftp client1@server — cd / should show only upload

1. Create the group and user

sudo groupadd sftponly
sudo useradd -m -g sftponly -s /usr/sbin/nologin client1
sudo passwd client1

-s /usr/sbin/nologin means no shell even if the SSH config were wrong. The password is for SFTP clients that can't use keys; for a key instead, put their public key in /home/client1/.ssh/authorized_keys as usual (owned by them, 600).

2. Build the jail

The chroot rule is strict: the jail folder and every folder above it must be owned by root and not writable by anyone else. Inside, make a folder the user owns.

sudo mkdir -p /srv/sftp/client1/upload
sudo chown root:root /srv/sftp /srv/sftp/client1
sudo chmod 755 /srv/sftp /srv/sftp/client1
sudo chown client1:sftponly /srv/sftp/client1/upload
sudo chmod 775 /srv/sftp/client1/upload

The user will see / as /srv/sftp/client1, with one folder in it: upload.

3. Configure sshd

sudo nano /etc/ssh/sshd_config

Make sure this line exists (it does by default on Ubuntu):

Subsystem sftp internal-sftp

and add at the very end of the file (Match blocks must be last):

Match Group sftponly
ChrootDirectory /srv/sftp/%u
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
PasswordAuthentication yes

%u is the username, so one block serves every member of the group. PasswordAuthentication yes inside the Match block lets these users use passwords even if you've turned them off globally (tutorial 3) — remove it if they'll use keys.

sudo sshd -t
sudo systemctl restart ssh

4. Test

From your laptop:

sftp client1@192.168.1.50
sftp> pwd
sftp> ls
sftp> cd upload
sftp> put test.txt

ls at the top should show only upload. And ssh client1@192.168.1.50 must be refused ("This service allows sftp connections only").

In WinSCP or FileZilla: protocol SFTP, port 22, the username and password. Phone apps: add an SFTP server with the same details.

5. Getting at the files yourself

Your own user can read /srv/sftp/client1/upload (it's 775 with group sftponly; add yourself to that group, or just use sudo). To share the uploads with a Docker app, mount that path as a volume.

More users

sudo useradd -m -g sftponly -s /usr/sbin/nologin camera
sudo passwd camera
sudo mkdir -p /srv/sftp/camera/upload
sudo chown root:root /srv/sftp/camera && sudo chmod 755 /srv/sftp/camera
sudo chown camera:sftponly /srv/sftp/camera/upload

No sshd change needed — the group does the work.

Troubleshooting

"Connection closed" immediately after authenticating. The chroot permissions are wrong. ls -ld /srv/sftp /srv/sftp/client1 — both must be root:root and not group/world writable. journalctl -u ssh -n 20 says "bad ownership or modes for chroot directory".

Can log in but can't upload. The upload folder isn't owned by the user.

They can see the whole server. The Match block isn't at the end of the file, or the user isn't in sftponly (groups client1).

Password refused. PasswordAuthentication yes isn't in the Match block while it's no globally.

Wrapping up

A group, a user with no shell, a root-owned jail with a user-owned folder inside, one Match block at the end of sshd_config. The outsider gets a folder; the rest of the server doesn't exist as far as they can tell.

Call us