SFTP Users

Each SFTP user is a password-only login that is chrooted to one site's files. It gets no shell, TTY or forwarding. Files it uploads are owned by the site's user (wp_<slug>), so WordPress keeps working.

How it works

Flow

The panel never runs system commands in the web request. FtpUserController saves the row with status = pending and dispatches SyncSftpUserJob. The job applies the change and sets status to active, or to failed with last_error. The list page refreshes itself while anything is pending. The password travels to the job encrypted, and the database stores only a hash.

wphoster:sync-sftp runs every 5 minutes. It restores bind mounts after a reboot, removes SFTP users whose row or site is gone, and rewrites the sshd file if needed. It only re-provisions rows that are active. Pending and failed rows are left for the job or the admin.

Key files

Config

KeyPurpose
wphoster.paths.sshd_config_snippetssshd include directory (/etc/ssh/sshd_config.d)
wphoster.paths.sftp_jails_rootJail root (/srv/wphoster-sftp)
wphoster.sftp.groupGroup every SFTP user belongs to (wphoster-sftp)
wphoster.sftp.config_fileManaged sshd file name (zz-wphoster-sftp.conf)
wphoster.sftp.ssh_servicesystemd unit to reload (ssh)
wphoster.sftp.host / .portConnection details shown in the panel (defaults: server IP, 22)

Prerequisites

The queue worker's user needs passwordless sudo for useradd, userdel, usermod, groupadd, chpasswd, mount, umount, mkdir, rmdir, chown, chmod, cp, mv, rm, cat, test, sshd -t and systemctl. The default sudoers in docs/DEPLOYMENT.md covers all of these. sshd_config must Include /etc/ssh/sshd_config.d/*.conf.