How to Set Up an SFTP Server on Debian 11/12

Step-by-step guide on how to set up an SFTP-only server on Debian 11/12 with chroot isolation.

In this guide, we are going to set up an SFTP-only server on Debian 11/12. We will also configure a chroot jail so SFTP users are restricted to a specific directory and cannot get a normal SSH shell.

SFTP (SSH File Transfer Protocol) runs over SSH, which provides encryption in transit. This makes it a safer alternative to legacy FTP, which transmits credentials and data in clear text unless wrapped with TLS.

Related Content

Prerequisites

To follow along this guide ensure you have the following:

  • A Debian 11 or Debian 12 server
  • Root access to the server or a user with sudo access
  • Internet access from the server

Ensuring that the server is up to date

Before proceeding, ensure your system is up to date. Use this command to refresh the system packages and update them.

1
2
sudo apt update
sudo apt upgrade -y

Ensuring that the SSH service is installed

Install OpenSSH server (this provides both SSH and SFTP):

1
2
3
4
5
6
sudo apt install -y openssh-server
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
openssh-server is already the newest version (1:8.4p1-5).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

Now that it is installed, enable and start the service:

1
sudo systemctl enable --now ssh

Confirm its status

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
sudo systemctl status ssh
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2021-12-03 10:18:03 UTC; 2 days ago
       Docs: man:sshd(8)
             man:sshd_config(5)
    Process: 665 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
   Main PID: 682 (sshd)
      Tasks: 1 (limit: 4626)
     Memory: 6.2M
        CPU: 9.132s
     CGroup: /system.slice/ssh.service
             └─682 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups

Dec 05 09:20:46 ip-10-2-40-122 sshd[14717]: Received disconnect from 221.181.185.94 port 47597:11:  [preauth]
Dec 05 09:20:46 ip-10-2-40-122 sshd[14717]: Disconnected from authenticating user root 221.181.185.94 port 47597 [preauth]
Dec 05 10:34:30 ip-10-2-40-122 sshd[14797]: Received disconnect from 222.186.30.76 port 23207:11:  [preauth]
Dec 05 10:34:30 ip-10-2-40-122 sshd[14797]: Disconnected from authenticating user root 222.186.30.76 port 23207 [preauth]
Dec 05 10:34:41 ip-10-2-40-122 sshd[14799]: Received disconnect from 221.181.185.151 port 18104:11:  [preauth]
Dec 05 10:34:41 ip-10-2-40-122 sshd[14799]: Disconnected from authenticating user root 221.181.185.151 port 18104 [preauth]
Dec 05 11:11:54 ip-10-2-40-122 sshd[14830]: Received disconnect from 221.131.165.65 port 19729:11:  [preauth]
Dec 05 11:11:54 ip-10-2-40-122 sshd[14830]: Disconnected from authenticating user root 221.131.165.65 port 19729 [preauth]
Dec 05 11:19:03 ip-10-2-40-122 sshd[14840]: Accepted publickey for admin from 105.231.148.146 port 60649 ssh2: RSA SHA256:nDQ1FMciYtGpPYjdOwbUTVg7kQxEFtAjoSdWulRilIA
Dec 05 11:19:03 ip-10-2-40-122 sshd[14840]: pam_unix(sshd:session): session opened for user admin(uid=1000) by (uid=0)

Create SFTP users and directories (chroot layout)

We will store SFTP users under /srv/sftp using this layout:

  • /srv/sftp/<username>: chroot directory (must be owned by root and not writable)
  • /srv/sftp/<username>/upload: writable directory owned by the user (where file uploads go)

Create the base directory:

1
2
3
sudo mkdir -p /srv/sftp
sudo chown root:root /srv/sftp
sudo chmod 755 /srv/sftp

Create an umbrella group for SFTP-only users:

1
sudo groupadd sftpusers

Create an SFTP-only user called citizix (no shell login):

1
sudo useradd -g sftpusers -d /upload -s /usr/sbin/nologin citizix

The above options do the following:

  • -g sftpusers: Set the primary group to sftpusers
  • -d /upload: Set the home directory (inside the chroot we will create)
  • -s /usr/sbin/nologin: Disable shell access (SFTP-only)
  • Finally, username citizix

Now create the chroot directory and the writable upload directory with correct ownership and permissions:

1
2
3
4
5
6
7
8
9
sudo mkdir -p /srv/sftp/citizix/upload

# Chroot directory must be owned by root and not writable by the user
sudo chown root:root /srv/sftp/citizix
sudo chmod 755 /srv/sftp/citizix

# Writable directory for uploads/downloads
sudo chown citizix:sftpusers /srv/sftp/citizix/upload
sudo chmod 755 /srv/sftp/citizix/upload

Then add password to the created user using this command:

1
2
3
4
sudo passwd citizix
New password:
Retype new password:
passwd: password updated successfully

Configure SSH for SFTP-only users (chroot)

Now we’ll configure OpenSSH so users in the sftpusers group:

  • can only use SFTP (no shell)
  • are chrooted to /srv/sftp/%u
  • land in /upload on login

Edit the SSH server config:

1
sudo vim /etc/ssh/sshd_config

Ensure the SFTP subsystem uses internal-sftp (this is often the default, but it’s safe to set explicitly):

1
Subsystem sftp internal-sftp

Add this content at the bottom of the file:

1
2
3
4
5
6
7
Match Group sftpusers
    X11Forwarding no
    AllowTcpForwarding no
    PermitTTY no
    ForceCommand internal-sftp -d /upload
    ChrootDirectory /srv/sftp/%u
    PasswordAuthentication yes

Then restart SSH to reload the config:

1
sudo systemctl restart ssh

Verify that SSH is running as expected:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
sudo systemctl status ssh
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2021-12-05 11:22:02 UTC; 12s ago
       Docs: man:sshd(8)
             man:sshd_config(5)
    Process: 15292 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
   Main PID: 15293 (sshd)
      Tasks: 1 (limit: 4626)
     Memory: 1.0M
        CPU: 159ms
     CGroup: /system.slice/ssh.service
             └─15293 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups

Dec 05 11:22:02 ip-10-2-40-122 systemd[1]: Starting OpenBSD Secure Shell server...
Dec 05 11:22:02 ip-10-2-40-122 sshd[15293]: Server listening on 0.0.0.0 port 22.
Dec 05 11:22:02 ip-10-2-40-122 sshd[15293]: Server listening on :: port 22.
Dec 05 11:22:02 ip-10-2-40-122 systemd[1]: Started OpenBSD Secure Shell server.

Verifying that the set up is working as expected

After successfully creating the user and adding SFTP configurations, let’s test the setup:

1
2
3
4
sftp citizix@18.236.122.10
citizix@18.236.122.10's password:
Connected to 18.236.122.10.
sftp>

Try a few commands:

1
2
3
4
sftp> pwd
sftp> ls -la
sftp> put local-file.txt
sftp> get remote-file.txt

If everything is configured correctly, the user will be restricted to the chroot and should start in /upload.

Adding shared directories inside the chroot (optional)

If you want users to access additional directories, they must still be inside the chroot (/srv/sftp/<username>/...). Also remember: the chroot directory itself must be root-owned and not writable.

Example: create a shared directory under the chroot and make a subdirectory writable:

Create the directory

1
2
3
4
sudo mkdir -p /srv/sftp/citizix/paymentfiles/upload
sudo chown root:root /srv/sftp/citizix/paymentfiles
sudo chmod 755 /srv/sftp/citizix/paymentfiles
sudo chown citizix:sftpusers /srv/sftp/citizix/paymentfiles/upload

That is it. Users should now have access.

Optional hardening tips

  • Disable direct root login over SSH by setting PermitRootLogin no in /etc/ssh/sshd_config.
  • If you only need SFTP for these accounts, keep shell access disabled (/usr/sbin/nologin) as shown above.
  • Consider using key-based authentication for admin users and restricting SSH access with AllowUsers/AllowGroups.

Conclusion

We managed to set up an SFTP-only server on Debian 11/12 in this guide, with chroot isolation and a dedicated upload directory for SFTP users.

Last updated on Jan 18, 2026 10:21 +0300
comments powered by Disqus
Citizix Ltd
Built with Hugo
Theme Stack designed by Jimmy