How to setup an SFTP server on CentOS

This tutorial explains how to setup and use an SFTP server on CentOS. Before I start, let me explain what actually SFTP represents and what it is used for. Currently, most people know that we can use normal FTP for transferring, downloading or uploading data from a server to client or client to server. But this protocol is getting hacked easily (if TLS is not used) by anonymous intruders as it the ports are wide open to anyone. Therefore, SFTP has been introduced to as another alternative to meet the main purpose to strengthen the security level.

SFTP stands for SSH File Transfer Protocol or Secure File Transfer Protocol. It uses a separate protocol packaged with SSH to provide a secure connection.

1. Preliminary Note

For this tutorial, I am using CentOS 7 in the 64bit version. The same steps will work on CentOS 6 as well. The tutorial result will show how a client can be provided with access to the SFTP server but unable to login to the server itself by SSH.

2. SFTP Installation

Unlike normal FTP, there's no need to install additional packages in order to use SFTP. We just require the prebuild SSHd package that got already installed during installation on the server. Therefore, just check to confirm if you already have the required SSH package. Below are the steps:


rpm -qa|grep ssh

The output should be similar to this:

[[email protected] ~]# rpm -qa|grep ssh

That's all, now we'll go on how to make the SFTP configuration.

3. SFTP Configuration

Once all prerequisites of installation are done, we'll step over to configuration phase. For best practice, we need to create a group and user so that we can manage all user that shall get SFTP access. But first, let's create an additional folder called data. Below are the steps:

mkdir -p /data/sftp
chmod 701 /data

Basically what I'm trying to do with the above step is to get a separate folder as main directory for the SFTP access. All user directories for the SFTP users will be subdirectories of this data folder.

Let's create a group for the SFTP user, below are the steps:

groupadd sftpusers

Then create a user 'howtoforge' and assign it to the SFTPUSERS group. Below are the steps:

useradd -g sftpusers -d /upload -s /sbin/nologin mysftpuser
passwd mysftpuser
Changing password for user mysftpuser.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.

Below is the explanation of the above commands:

  1. I create a user and include the user into sftpusers group using -g command.
  2. I assign the main directory for the user to be in the /upload directory by setting the -d /upload command. This means that later the /upload folder will be under /data/mysftpuser/upload.
  3. I limit the access to the /sbin/nologin shell to ensure the user is only able to use the SFTP protocol, not SSH.
  4. I name the user "mysftpuser".
  5. Set password for user "mysftpuser".

Now let's create the /upload folder under /data/mysftpuser, then assign appropriate ownership to the folder.

mkdir -p /data/mysftpuser/upload
chown -R root:sftpusers /data/mysftpuser
chown -R mysftpuser:sftpusers /data/mysftpuser/upload

Once done, verify that the new folder under the directory /data exists and that we made the configuration correct.

[[email protected] ~]# ls -ld /data/
drwx-----x. 5 root root 54 Mar 22 14:29 /data/
[[email protected] ~]# ls -ld /data/mysftpuser
drwxr-xr-x. 3 root sftpusers 20 Mar 22 14:29 /data/mysftpuser
[[email protected] ~]# ls -ld /data/mysftpuser/upload
drwxr-xr-x. 2 mysftpuser sftpusers 6 Mar 22 14:29 /data/mysftpuser/upload
[[email protected] ~]# cat /etc/passwd|grep mysftpuser

Now configure the SSH protocol to create an SFTP process. This can be done by editing the configuration file under /etc/ssh/sshd_config. Below are the steps:

nano /etc/ssh/sshd_config

Add the following lines at the end of the file.

Match Group sftpusers
ChrootDirectory /data/%u
ForceCommand internal-sftp

Once done restart the SSH services, below are the steps:

service sshd status
Redirecting to /bin/systemctl status sshd.service
? sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2018-03-22 14:21:38 CET; 16min ago
Docs: man:sshd(8)
Main PID: 942 (sshd)
CGroup: /system.slice/sshd.service
??942 /usr/sbin/sshd -D
Mar 22 14:21:37 localhost.localdomain systemd[1]: Starting OpenSSH server daemon...
Mar 22 14:21:38 localhost.localdomain sshd[942]: Server listening on port 22.
Mar 22 14:21:38 localhost.localdomain sshd[942]: Server listening on :: port 22.
Mar 22 14:21:38 localhost.localdomain systemd[1]: Started OpenSSH server daemon.
Mar 22 14:21:49 localhost.localdomain sshd[1375]: Accepted password for root from port 59465 ssh2
service sshd restart
[[email protected] ~]# service sshd restart
Redirecting to /bin/systemctl restart sshd.service

4. Testing SFTP

Now everything has been configured, so let's make a test to ensure the setup meets our purpose.
I'll access SFTP by using another server called TEST01. First, I'll verify the Port of the SFTP server . To do that I'll use the nmap function. If your client server didn't have it you may download and install it with yum as shown below:

yum list nmap
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
* base:
* epel:
* extras:
* remi-php72:
* remi-safe:
* updates:
Available Packages
nmap.x86_64 2:6.40-7.el7
yum install nmap -y
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
* base:
* epel:
* extras:
* remi-php72:
* remi-safe:
* updates:
Resolving Dependencies
--> Running transaction check
---> Package nmap.x86_64 2:6.40-7.el7 will be installed
--> Processing Dependency: nmap-ncat = 2:6.40-7.el7 for package: 2:nmap-6.40-7.el7.x86_64
--> Running transaction check
---> Package nmap-ncat.x86_64 2:6.40-7.el7 will be installed
--> Finished Dependency Resolution
Dependencies Resolved
Package Arch Version Repository Size
nmap x86_64 2:6.40-7.el7 base 4.0 M
Installing for dependencies:
nmap-ncat x86_64 2:6.40-7.el7 base 201 k
Transaction Summary
Install 1 Package (+1 Dependent package)
Total download size: 4.2 M
Installed size: 17 M
Downloading packages:
(1/2): nmap-ncat-6.40-7.el7.x86_64.rpm | 201 kB 00:00:01
(2/2): nmap-6.40-7.el7.x86_64.rpm | 4.0 MB 00:00:14
Total 300 kB/s | 4.2 MB 00:00:14
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
Installing : 2:nmap-ncat-6.40-7.el7.x86_64 1/2
Installing : 2:nmap-6.40-7.el7.x86_64 2/2
Verifying : 2:nmap-ncat-6.40-7.el7.x86_64 1/2
Verifying : 2:nmap-6.40-7.el7.x86_64 2/2
nmap.x86_64 2:6.40-7.el7
Dependency Installed:
nmap-ncat.x86_64 2:6.40-7.el7
[[email protected] ~]# nmap -n
Starting Nmap 6.40 ( ) at 2018-03-22 14:51 CET
Nmap scan report for
Host is up (0.000016s latency).
Not shown: 997 closed ports
22/tcp open ssh
Nmap done: 1 IP address (1 host up) scanned in 0.12 seconds

You'll notice that currently on our SFTP server, the only open port is SSH 22. Now, let's try to access the SFTP server (IP: in my case) from TEST01 client. Below are the steps:

[[email protected] /]# sftp [email protected]
[email protected]'s password:
Connected to
sftp> pwd
Remote working directory: /upload

Great! Now our SFTP server is accessible from outside. Notice that the default directory is /upload . This means that SFTP will only show the default path as /upload even though our previous configuration made in the SFTP server directory is /data/mysftpuser/upload.
Now let's try to get a file from the SFTP server directory into our testing client. First, let's create a test file under /data/mysftpuser/upload. Below are the steps:

cd /data/mysftpuser/upload
touch testing_file.txt

Then go back to our testing site TEST01 and see if we able to get and download the created file.

[[email protected] /]# sftp [email protected]
[email protected]'s password:
Connected to
sftp> pwd
Remote working directory: /upload
sftp> ls
sftp> get testing_file.txt
Fetching /upload/testing_file.txt to testing_file.txt
sftp> quit

Excellent! Our SFTP test has been successful, let's try to access SSH using the user mysftpuser. As previously, we've set configuration as /sbin/nologin, therefore the user won't be able to use SSH services:

[[email protected] ~]# ssh [email protected]
[email protected]'s password:
This service allows sftp connections only.
Connection to closed.

Nice! Now we have a secured SFTP server up and running.

Share this page:

Suggested articles

24 Comment(s)

Add comment


By: Chandrakumar Muthaiah

Looks great but you need to change this line from

chown -R shahrilk:sftpusers /data/shahrilk


chown -R root:root /data/shahrilk

as it is needed for the chroot to work or sshd will produce permission denied.


By: shahril bin kamaruzzaman

Nice heads up! 

By: Jere

I did it, but it still produce  Permission denied, please try again.

By: greenears

Hey thanks for your tutorial , but I have a problem. When I try to test it using the sftp [email protected] it gives me this error ' Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password). Couldnt read packet: Connection reset by peer '

Maybe something with the ssh_config file ?


By: JJ

WoW... Thanks for explaination. i will keep to visit this website to learn...

By: ws

Nice write up!!!  Is it possible to add additional configurations such as block IP addresses after X numbers of incorrect login?

Thanks in advance.

By: Paul

Thanks for your article. I was able to set it up.


For CentOS7 (my environment) -- I will agree with Chandrakumar's comment that the ownership of /data/shahrilk is not correct in  your article. 

The path /data/shahrilk must be root owned and writable only by root.

In your article, I would simply remove/skip the line:

chown -R shahrilk:sftpusers /data/shahrilk


Also, it will be useful to include a tip to look at /var/log/secure if readers are having trouble getting going. This will show connection attempts and any errors with authentication or setup.

Further, on CentOS 7, the line in /etc/ssh/sshd_config already exists, with no Match rules.... and show up slighly differently as:

Subsystem sftp internal-sftp    ? on CentOS 7, this appear as: /usr/libexec/openssh/sftp-server

And so you only need to add the rules at the end of the file, namely (only these lines)

Match Group sftpusersChrootDirectory /data/%uForceCommand internal-sftp

By: Gatsu

Merci beaucoup mon ami. Tu es trés bon

By: Robert


Thanks for this tutorial. It's great and it worked for me. I installed SFTP server and it already works.But I still have one question...

What is the purpose of /data/sftp directory if all users upload their files to /data/%u/upload and /data/sftp is hidden from them?Why do we need it?

By: Robert


Is this a mistake

useradd -g sftpusers -d /upload -s /sbin/nologin mysftpuser

Should it be

useradd -g sftpusers -d /data/myftpuser/upload -s /sbin/nologin mysftpuser

Because when I made the second user I got an error that "home directory already exist".

By: Stefan

useradd -M -g sftpusers -d /upload -s /sbin/nologin mysftpuser

Add -M to not try to create "fake" directory


By: Pete Hope

Thanks for the tutorial. It seems there's an omission (at least on my system).Following the instructions and attempting to connect a client resulted in:

"Permission denied (publickey,gssapi-keyex,gssapi-with-mic)." 

I assume the server was expecting an SSH Key?

Solution:Edit /etc/ssh/sshd_config :

PasswordAuthentication yes

This allowed the client to log in with a password.

Thanks again

By: Zai

Great explanation. All went as suggested in the steps listed.My username is set to zaiftp and password is accepted.

However at final step of testing the SFTP service, it failed with message :

[[email protected] data]# sftp [email protected]@'s password:Write failed: Broken pipeCouldn't read packet: Connection reset by peer

My local IP is :

I did :  grep zaiftp /etc/passwd

and got :zaiftp:x:1005:1005::/data/sftp/upload/:/sbin/nologin

So the user dir /data/sftp/upload/ seems to have been allocated fine.

Help appreciated!



By: rohi

Best article to setup sftp server on any centos machine. Perfect step by step instructions are given. Thank you so much.

By: thelinuxwiz

Change -g to -G to add the user to a secondary group 

useradd -G sftpusers -d /upload -s /sbin/nologin mysftpuser


By: Jake


Do you use this as an internal SFTP Server only? Is a setup like this secure enough to put this on public internet if only port 22 is permitted? 

By: drakkan

You can also try SFTPGo

it has chroot support builtin, virtual quota, atomic uploads and many other features.

It can execute configurable custom commands and/or HTTP notifications on upload, download, delete or rename.

It is written in Go, so no runtime dependencies, and it works on Windows too



By: Marc Nevin

What options exist to manage multiple connections and ensuring no single connection can take over the entire network bandwidth provided by OS/network interface ? We have multiple clients uploading during a perscribed window of time and sometime a client will get very poor (read slow) connection.   I need to balance the bandwidth among the total number of active connections while net over budening the network connection to the hosting server. 

Great article !! 

By: mcajina

When I try to connect to the SFTP server via FileZilla, I get a warning that the "server's host key is unknown," and it wants confirmation to trust the key. It then tries to connect, but fails with "Error: Connection reset by peer."

I also tried it via Terminal from my computer and I also got a warning about the "ECDSA key fingerprint." After I gave permission to continue, it also failed with a "Broken pipe" message. Same thing if I try from the server itself @localhost

My sshd_config file has the following edits, but everything else in the file that was uncommented I left alone:

PermitRootLogin noPasswordAuthentication yesSubsystem    sftp    internal-sftpMatch Group sftpusersChrootDirectory /ftpserver/%uForceCommand internal-sftpPermitTunnel noAllowAgentForwarding noAllowTcpForwarding noX11Forwarding no

When I set up a test sftp server a few months back following these instructions, it worked perfectly. Now that I need to set up a real one, it's failing to allow the user account to connect. Any ideas why I'm getting these errors now?

By: mcajina

Nevermind, I figured it out. I accidentally changed the ownership of the folder with the username to that user. It's supposed to be owned by root.

By: Bas

Thanks for this great article!

For security reasons its wise to change the port from the default 22 to something else. If you want to connect to it using SFTP, use:

sftp -oPort=1234 [email protected]

Where 1234 you write your own port of course.

By: Andrew Bruce

Dude! You actually specified nmap to test - you completely rock. To make up for the other comments - your instructions had me going in (I timed it) 8 minutes.


Thanks so much for saving me from my local neural net failures. And for specifying nmap as a verification test...I cannot tell you how pleased that makes me. For all of you out there - *learn nmap* as that and ncat are truly, truly your friends.

By: Andrew Bruce

One problem - this loses in many situations where you must connect over Someone Else's Network. So many time, any ports other than 80 / 443 / 22 simply get filtered out and denied egress. Don't get me wrong, I agree with you in principle (although port knocking is even more effective if you want security as *nmap the fabulous tool* will quickly figure out that 1234 is also being used) - but in practice using non-standard ports as security via obscurity has bitten me when I couldn't connect from an arbitrary endpoint where I happened to be stuck (such as in the hotel...). But rock on with the security - good stuff!

By: Camilo Luna

Works perfect for me. Thank's, it was very helpful.