Back to tutorials
Tutorial

SFTP Setup Guide Tutorial (2026): Lock Down File Transfers on a Hosting VPS (Chroot, Keys, and Per-User Limits)

SFTP setup guide tutorial for 2026: secure SSH file access with chroot, keys, and safe permissions on a hosting VPS.

By Anurag Singh
Updated on Jul 03, 2026
Category: Tutorial
Share article
SFTP Setup Guide Tutorial (2026): Lock Down File Transfers on a Hosting VPS (Chroot, Keys, and Per-User Limits)

FTP is still one of the quickest ways to get a hosting server compromised. Plain FTP sends credentials in cleartext. “FTP over TLS” (FTPS) is often misconfigured, or quietly blocked by firewalls.

In 2026, the sane default is SFTP (SSH File Transfer Protocol). Use key-based logins, strict directory isolation, and hard limits. This SFTP setup guide tutorial walks through a production-ready setup for Ubuntu 24.04/26.04 and Debian 12/13 on a VPS used for websites, WordPress, or reseller-style access.

By the end, you’ll have per-user SFTP-only access with optional chroot jails. You’ll also have upload directories that work without permission drama.

Finally, you’ll add guardrails against common abuse: brute-force attempts, disk bloat, and lateral movement.

Prerequisites and a quick decision: chroot vs non-chroot

Before you edit anything, decide whether you actually need a chroot jail.

  • Use chroot if you sell hosting/reseller access, you have multiple customers, or you want “this user can only see their web root” enforced by SSH.
  • Skip chroot if this is a single internal developer account and you already isolate apps with separate Unix users and tight permissions.

This guide assumes you’re root (or a sudo-capable user) on a Linux VPS.

If you’re still picking infrastructure, start with a HostMyCode VPS so you can control SSH, firewall rules, and user isolation end to end.

OS and package baseline

  • Ubuntu 24.04 LTS / Ubuntu 26.04 LTS (or Debian 12/13)
  • OpenSSH server (package: openssh-server)

Know your SSHD config paths

  • Main config: /etc/ssh/sshd_config
  • Included snippets (Ubuntu): /etc/ssh/sshd_config.d/*.conf
  • Systemd service: systemctl status ssh (Ubuntu/Debian name is usually ssh)

Step 1 — Install OpenSSH and verify SFTP subsystem

Most VPS images already ship with OpenSSH. Verify it first. Install it only if needed:

sudo apt update
sudo apt install -y openssh-server
sudo systemctl enable --now ssh

Confirm the SFTP subsystem is available:

sudo sshd -T | grep -i subsystem

You should see something like:

subsystem sftp /usr/lib/openssh/sftp-server

On current distros, the in-process implementation is usually the better choice. It runs inside sshd and gives you more control:

Subsystem sftp internal-sftp

Step 2 — Create an SFTP-only group and user

Create a dedicated group for SFTP-only accounts. This gives you one place to apply SSH rules with a Match Group block.

sudo groupadd sftpusers

Create a user. Replace client1 with the real username:

sudo useradd -m -s /usr/sbin/nologin -G sftpusers client1
sudo passwd client1

Why /usr/sbin/nologin? It blocks interactive shell access. SFTP still works once you force the SFTP command in your SSH config.

Step 3 — Build a chroot directory that doesn’t break uploads

Chroot has one non-negotiable rule. The chroot root directory must be owned by root and not writable by the user.

If you ignore that, you’ll hit the classic “Connection closed” or “Permission denied” loop.

A layout that behaves well:

  • /sftp/client1 (root-owned chroot)
  • /sftp/client1/uploads (user-writable upload area)
  • /sftp/client1/www (optional bind-mount to your site docroot)
sudo mkdir -p /sftp/client1/uploads
sudo chown root:root /sftp/client1
sudo chmod 755 /sftp/client1

sudo chown client1:sftpusers /sftp/client1/uploads
sudo chmod 750 /sftp/client1/uploads

If you host WordPress at /var/www/client1/public, you can bind-mount it into the chroot.

Choose read/write, or use read-only if you want to be stricter:

sudo mkdir -p /var/www/client1/public
sudo mkdir -p /sftp/client1/www
sudo mount --bind /var/www/client1/public /sftp/client1/www

Persist the bind mount in /etc/fstab:

/var/www/client1/public  /sftp/client1/www  none  bind  0  0

Pitfall: If your web stack runs as www-data, plan permissions so SFTP uploads don’t leave PHP/WordPress unable to write.

A common small-hosting pattern is:

  • Make the site directory group www-data
  • Add the SFTP user to that group
  • Use setgid so new files inherit the right group
sudo usermod -aG www-data client1
sudo chgrp -R www-data /var/www/client1/public
sudo find /var/www/client1/public -type d -exec chmod 2775 {} \;
sudo find /var/www/client1/public -type f -exec chmod 664 {} \;

The 2 in 2775 sets the setgid bit. New files inherit the directory group.

This prevents many “WordPress can’t update” incidents after SFTP uploads.

Step 4 — Configure sshd for SFTP-only + chroot + key auth

Back up your SSH config before you touch it:

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)

Open /etc/ssh/sshd_config. On Ubuntu, you can also create a drop-in like /etc/ssh/sshd_config.d/50-sftp.conf.

Make sure you have:

Subsystem sftp internal-sftp

Then add this Match Group block at the end:

Match Group sftpusers
    ChrootDirectory /sftp/%u
    ForceCommand internal-sftp
    AllowTCPForwarding no
    X11Forwarding no
    PermitTunnel no
    PasswordAuthentication no
    PubkeyAuthentication yes

Why disable TCP forwarding? On a hosting VPS, port forwarding becomes a convenient pivot if an account gets compromised.

If you truly need SSH tunnels, keep that capability in a separate admin group and handle it deliberately (see SSH port forwarding tutorial).

Add an extra guardrail: restrict which users can SSH at all

If your server has a small, known list of SSH users, declare it explicitly. This blocks surprise accounts even if they exist.

AllowUsers root adminuser client1

If you add this, don’t lock yourself out. Keep an active root session open while you test.

Step 5 — Add SSH keys for the SFTP user (and disable passwords)

On your local machine, generate a modern key (ed25519):

ssh-keygen -t ed25519 -a 64 -f ~/.ssh/client1_sftp

Copy the public key to the server. This works if password auth is still temporarily enabled:

ssh-copy-id -i ~/.ssh/client1_sftp.pub client1@YOUR_SERVER_IP

If ssh-copy-id isn’t convenient, add the key manually:

sudo mkdir -p /home/client1/.ssh
sudo nano /home/client1/.ssh/authorized_keys

sudo chown -R client1:client1 /home/client1/.ssh
sudo chmod 700 /home/client1/.ssh
sudo chmod 600 /home/client1/.ssh/authorized_keys

Validate your sshd config syntax:

sudo sshd -t

Restart SSH:

sudo systemctl restart ssh

Step 6 — Test SFTP and verify the jail

From your local machine:

sftp -i ~/.ssh/client1_sftp client1@YOUR_SERVER_IP

Inside the SFTP prompt:

pwd
ls -la
put somefile.zip uploads/

You should land inside the chroot and only see what you created (for example uploads and www).

If you can browse /etc or /var, the chroot isn’t taking effect.

Quick diagnostics for common failures

  • “Connection closed” right after login: the chroot ownership/permissions are wrong. /sftp/client1 must be root:root with mode 755.
  • “Permission denied” on upload: you’re trying to write into the root-owned chroot. Upload into uploads/ or a writable bind mount.
  • Key rejected: re-check permissions on ~/.ssh and authorized_keys, then look at the logs.

Check logs on Ubuntu/Debian:

sudo journalctl -u ssh --since "10 minutes ago" -n 200

Step 7 — Add per-user limits: stop “disk bloat by upload” incidents

SFTP accounts get abused in predictable ways. Think giant archives, “temporary” backups parked in web roots, and media dumps that never get cleaned up.

Set a hard ceiling now. Don’t wait for the first outage.

Option A: disk quotas (practical on single-volume VPS)

On Ubuntu/Debian, quotas are straightforward as long as your filesystem supports them (ext4 usually does).

Install tools:

sudo apt install -y quota

Edit /etc/fstab and add usrquota,grpquota for the filesystem that holds /sftp (often /). Example:

UUID=xxxx  /  ext4  defaults,usrquota,grpquota  0  1

Remount and initialize:

sudo mount -o remount /
sudo quotacheck -cum /
sudo quotaon /

Set a quota for client1 (example: 5 GB soft / 6 GB hard):

sudo setquota -u client1 5G 6G 0 0 /
sudo quota -u client1

Option B: keep web roots clean (policy + automation)

Quotas help, but you still want basic hygiene. If usage starts creeping up, use a safe cleanup process instead of deleting blindly.

This pairs well with this VPS cleanup tutorial.

Step 8 — Reduce attack surface: firewall + brute-force controls

SFTP rides on SSH. If SSH is reachable from the internet, it will get scanned. That’s normal.

Your job is to limit exposure and shut down noisy abuse quickly.

Lock down SSH with UFW (simple and effective)

If you use UFW, allow only what you actually need. Example for a typical hosting VPS:

sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose

If you want a safer baseline without breaking web or mail ports, follow our UFW firewall configuration tutorial and then return here.

Detect brute-force attempts early

At minimum, monitor SSH auth failures and block repeated attempts.

If you’re seeing lockouts or suspicious patterns, start with targeted SSH triage: SSH brute-force troubleshooting.

Step 9 — Make SFTP usable for WordPress and agencies (without giving away SSH)

Agencies usually need a narrow set of capabilities. They might upload a theme, push a plugin update package, grab a log, or drop in a verification file.

You can support that without handing out shell access. Separate roles cleanly:

  • SFTP user writes only to the site directory (and maybe uploads).
  • Admin user has shell access and can run updates, backups, and debugging.
  • Backups go offsite, not into the same folder the SFTP user can fill up.

If you’re building a WordPress hosting setup on a VPS, keep SSL predictable and automated.

Use a process you can repeat across sites: SSL setup guide.

Step 10 — Operational checklist (copy/paste for your runbook)

  • Create group sftpusers and user with /usr/sbin/nologin
  • Chroot root dir owned by root:root, mode 755
  • Writable folder inside chroot (e.g. uploads/) owned by the user
  • Subsystem sftp internal-sftp
  • Match Group sftpusers with ForceCommand internal-sftp
  • Disable password auth for SFTP users; use ed25519 keys
  • Disable TCP forwarding for SFTP users
  • Set quotas or enforce storage policy
  • UFW allows only required ports; SSH monitored for brute-force
  • Test with a real SFTP client before handing credentials to a customer

Where HostMyCode fits: clean SFTP on a VPS you can actually control

Shared hosting works for simple sites. The moment you need strict file-transfer rules, chrooted users, and SSH key enforcement, you’ll want a VPS.

A HostMyCode VPS gives you root access for OpenSSH, firewalling, and user isolation.

If you don’t want to own patching, SSH policy upkeep, and backup checks, managed VPS hosting is the practical middle ground.

If you’re setting up SFTP for clients or an agency team, use a VPS with predictable performance and full SSH control. HostMyCode’s VPS plans are a strong fit for chrooted SFTP, WordPress sites, and multi-user access. If you want us to handle the ongoing work (updates, hardening baselines, and recovery planning), choose managed VPS hosting.

FAQ: SFTP setup on hosting servers

Is SFTP the same as FTPS?

No. SFTP runs over SSH (port 22 by default). FTPS is FTP with TLS and typically needs multiple ports plus careful firewall/NAT handling.

Can I give a user SFTP access without shell access?

Yes. Set their shell to /usr/sbin/nologin and use ForceCommand internal-sftp in a Match block.

Why does chroot require a root-owned directory?

OpenSSH enforces this so users can’t modify the jail environment. If the chroot root is writable, escaping becomes easier.

What’s the safest way to let users upload into a WordPress site?

Use a chroot with a bind mount to the site directory, then enforce group ownership and setgid on directories so files stay writable by the web server.

How do I investigate failed SFTP logins?

On Ubuntu/Debian, use journalctl -u ssh and look for key/permission errors. Most failures come down to authorized_keys permissions or chroot ownership.

Summary

SFTP only stays “secure by default” if you finish the job. Use keys instead of passwords, create SFTP-only accounts, and add chroot isolation for customer access.

Add limits that prevent upload abuse. Then document the pattern and reuse it for every new site or client.

For multi-user access with predictable performance, run it on a HostMyCode VPS and keep file transfers clean and auditable.