
如果你使用 DeepSeek、元宝 AI、Copilot、ChatGPT 等 AI 工具,协助配置轩辕镜像、编写 docker pull 命令、修改 Docker Compose 镜像地址、配置镜像加速、排查镜像拉取失败、分析报错日志等问题,请先让 AI 阅读并遵守轩辕镜像的规则文档。
只需在 AI 对话中先发送下面这句话即可:
请先完整阅读并严格遵守以下文档中的全部规则与要求:
https://xuanyuan.cloud/agents.md
在未充分阅读并理解该文档前,不要生成任何命令、配置、修改建议、故障排查方案或技术回答。后续所有输出都必须严格以该文档中的规范为最高优先级执行。查看 agents.md 用法指南与完整示范。国内用户首推 元宝 AI、DeepSeek 的深度思考模式,不推荐豆包 AI;Cursor 等编辑器可在对话 @ 该链接,或加入 User Rules。 若 AI 无法访问外链,可 打开说明文档 复制全文粘贴。文档会随站点更新,复制内容可能过期,建议定期检查。
https://img.shields.io/badge/GitHub-Source-blue?logo=github](https://github.com/pacnpal/simple-sftp-server) https://img.shields.io/badge/GHCR-Package-blue?logo=github](https://ghcr.io/pacnpal/simple-sftp-server) https://img.shields.io/badge/Docker%20Hub-Image-blue?logo=docker](https://hub.docker.com/r/pacnpal/simple-sftp-server)
A dead simple SFTP server in Docker. Nothing else. No shell, no tunneling, no forwarding. Just SFTP.
These are pre-built and hosted. Pick one — they're the same image:
pacnpal/simple-sftp-server
ghcr.io/pacnpal/simple-sftp-server
Create host directories first (recommended):
bashmkdir -p /home/user/.ssh_keys/simple-sftp /home/user/.ssh_keys/simple-sftp-host /home/user/sftp_data chmod 700 /home/user/.ssh_keys /home/user/.ssh_keys/simple-sftp /home/user/.ssh_keys/simple-sftp-host
bashdocker run -d --name simple-sftp-server \ --user "$(id -u):$(id -g)" \ --cap-add SYS_CHROOT \ -p 2222:2022 \ -e PUID="$(id -u)" \ -e PGID="$(id -g)" \ -e SSH_KEY_DIR=/keys \ -e HOST_KEY_DIR=/host_keys \ -e SFTP_CHROOT=true \ -v /home/user/.ssh_keys/simple-sftp:/keys \ -v /home/user/.ssh_keys/simple-sftp-host:/host_keys \ -v /home/user/sftp_data:/home/sftpuser/data \ pacnpal/simple-sftp-server
Recommended mode is jailed (SFTP_CHROOT=true).
Chroot mode confines authenticated users to /home/sftpuser, which reduces accidental exposure of unrelated mounted paths.
Chroot mode requires SYS_CHROOT capability at runtime.
This project is provided as-is, without warranty or support. Operators are responsible for secure deployment.
This image intentionally stores generated client keys on the host for convenience. You are responsible for securing /home/user/.ssh_keys/simple-sftp.
Use non-overlapping host directories for /keys, /host_keys, and /home/sftpuser/data.
On first start, a keypair is generated. The private key is already on your host:
bashcp /home/user/.ssh_keys/simple-sftp/sftpuser_key ./sftp_key && chmod 600 ./sftp_key
Windows users: The
chmodcommand doesn't exist on Windows. Instead, right-click the file > Properties > Security > make sure only your user has access.
bashsftp -i sftp_key -P 2222 sftpuser@localhost
You should see:
Connected to sftpuser@localhost. sftp>
You're in. Type ls to look around, put myfile.txt to upload, get myfile.txt to download, or quit to disconnect.
bash# Stop the server docker stop simple-sftp-server # Start it back up (your files and keys are still there) docker start simple-sftp-server # Remove it completely docker rm -f simple-sftp-server
Your files and keys are stored on the host filesystem, so they persist across stop, start, and even docker rm + recreate. They only go away if you explicitly delete the directories:
bash# WARNING: This deletes all generated keys, host keys, and uploaded files rm -rf /home/user/.ssh_keys/simple-sftp /home/user/.ssh_keys/simple-sftp-host /home/user/sftp_data
If you prefer Docker Compose, create a file called docker-compose.yml:
yamlservices: sftp: image: pacnpal/simple-sftp-server:latest user: "${PUID:-1000}:${PGID:-1000}" cap_add: - SYS_CHROOT ports: - "${SFTP_HOST_PORT:-2222}:2022" environment: - PUID=1000 - PGID=1000 - SSH_KEY_DIR=/keys - HOST_KEY_DIR=/host_keys - SFTP_CHROOT=true volumes: - /home/user/.ssh_keys/simple-sftp:/keys - /home/user/.ssh_keys/simple-sftp-host:/host_keys - /home/user/sftp_data:/home/sftpuser/data
Then run:
bashdocker compose up -d
Everything else is the same — get the key and connect as described above.
If you already have SSH keys and an authorized_keys file, you can skip the auto-generated key.
Put authorized_keys inside a host directory, then mount that directory as SSH_KEY_DIR.
bashdocker run -d --name simple-sftp-server \ --user "$(id -u):$(id -g)" \ --cap-add SYS_CHROOT \ -p 2222:2022 \ -e PUID="$(id -u)" \ -e PGID="$(id -g)" \ -e SSH_KEY_DIR=/keys \ -e HOST_KEY_DIR=/host_keys \ -e SFTP_CHROOT=true \ -v /path/to/your/keydir:/keys \ -v /home/user/.ssh_keys/simple-sftp-host:/host_keys \ -v /home/user/sftp_data:/home/sftpuser/data \ pacnpal/simple-sftp-server
Replace /path/to/your/keydir with the directory containing authorized_keys.
authorized_keys is copied at startup to /tmp/simple-sftp-runtime/authorized_keys with strict runtime permissions.HOST_KEY_DIR (/host_keys by default). Missing host key types are auto-generated on startup.authorized_keys can be mounted read-only; write access is only required for first-run key generation.sshd template at build time (see sshd_config.template in this repo).sshd -D -e -f /tmp/simple-sftp-runtime/sshd_config.runtimeAuthorizedKeysFile is pinned to /tmp/simple-sftp-runtime/authorized_keys to avoid host mount permission edge cases.2022; change only the host publish port (-p <host_port>:2022).SFTP_CHROOT=true (default/recommended): enables ChrootDirectory /home/sftpuser.SFTP_CHROOT=false: disables filesystem jail.CAP_SYS_CHROOT (--cap-add SYS_CHROOT / Compose cap_add: [SYS_CHROOT])./home/sftpuser.If this fails even with the right private key:
bashsftp -o IdentitiesOnly=yes -o IdentityAgent=none -i sftp_key -P 2222 sftpuser@localhost
Verify the runtime sshd view:
bashdocker exec <container_name> sh -lc 'sshd -T -C user=sftpuser,host=localhost,addr=127.0.0.1 | grep authorizedkeysfile' docker exec <container_name> sh -lc 'ssh-keygen -lf /tmp/simple-sftp-runtime/authorized_keys' ssh-keygen -y -f sftp_key | ssh-keygen -lf -
The authorizedkeysfile path should be /tmp/simple-sftp-runtime/authorized_keys, and the two fingerprints should match.
server lacks privileges to chroot to ChrootDirectoryThis means SFTP_CHROOT=true but the container does not have CAP_SYS_CHROOT.
Fix:
docker run: add --cap-add SYS_CHROOTyamlcap_add: - SYS_CHROOT
If your runtime policy does not allow this capability, set SFTP_CHROOT=false and accept the no-chroot risk model.
If clients warn about host key changes, your host-key mount is not persisting stable key files.
bashdocker inspect <container_name> --format '{{range .Mounts}}{{.Destination}} <- {{.Source}}{{println}}{{end}}' ls -l /home/user/.ssh_keys/simple-sftp-host/ssh_host_*
Keep /host_keys mounted to a stable host directory and avoid overlapping it under an SFTP-exposed parent mount.
If logs include errors like:
textcp: can't open '/host_keys/ssh_host_rsa_key': Permission denied
the mounted key directories are not readable/writable by the container.
HOST_KEY_DIR and SSH_KEY_DIR mounts are readable/writable during startup.PUID/PGID.You can customize behavior with these:
| Variable | Default | What it does |
|---|---|---|
SFTP_PATHS | /data | Comma-separated directories to create (accessible over SFTP) |
SSH_KEY_DIR | /keys | Where user keys are stored inside the container |
HOST_KEY_DIR | /host_keys | Where SSH host keys are persisted inside the container |
PUID | SFTP_UID (1000 by default) | Runtime UID to run the container as |
PGID | SFTP_GID (1000 by default) | Runtime GID to run the container as |
SFTP_CHROOT | true | true enables filesystem jail (ChrootDirectory) |
SSH_KEY_DIR and HOST_KEY_DIR should be absolute paths. For backward compatibility, relative values like keys are interpreted as /keys.
To match host filesystem ownership, pass PUID and PGID at container startup:
bashdocker run -d --name simple-sftp-server \ --user "$(id -u):$(id -g)" \ --cap-add SYS_CHROOT \ -p 2222:2022 \ -e PUID="$(id -u)" \ -e PGID="$(id -g)" \ -e SSH_KEY_DIR=/keys \ -e HOST_KEY_DIR=/host_keys \ -e SFTP_CHROOT=true \ -v /home/user/.ssh_keys/simple-sftp:/keys \ -v /home/user/.ssh_keys/simple-sftp-host:/host_keys \ -v /home/user/sftp_data:/home/sftpuser/data \ pacnpal/simple-sftp-server
PUID/PGID must match the container runtime user (user: / --user).
Example — serve multiple directories:
bashdocker run -d --name simple-sftp-server \ --user "$(id -u):$(id -g)" \ --cap-add SYS_CHROOT \ -p 2222:2022 \ -e PUID="$(id -u)" \ -e PGID="$(id -g)" \ -e SSH_KEY_DIR=/keys \ -e HOST_KEY_DIR=/host_keys \ -e SFTP_CHROOT=true \ -e SFTP_PATHS=/data,/uploads,/backups \ -v /home/user/.ssh_keys/simple-sftp:/keys \ -v /home/user/.ssh_keys/simple-sftp-host:/host_keys \ -v /home/user/sftp_data:/home/sftpuser/data \ -v /home/user/sftp_uploads:/home/sftpuser/uploads \ -v /home/user/sftp_backups:/home/sftpuser/backups \ pacnpal/simple-sftp-server
Use this only when you intentionally accept a wider filesystem access model for authenticated users.
bashdocker run -d --name simple-sftp-server \ --user "$(id -u):$(id -g)" \ -p 2222:2022 \ -e PUID="$(id -u)" \ -e PGID="$(id -g)" \ -e SSH_KEY_DIR=/keys \ -e HOST_KEY_DIR=/host_keys \ -e SFTP_CHROOT=false \ -v /home/user/.ssh_keys/simple-sftp:/keys \ -v /home/user/.ssh_keys/simple-sftp-host:/host_keys \ -v /home/user/sftp_data:/home/sftpuser/data \ pacnpal/simple-sftp-server
Compose equivalent:
yamlservices: sftp: image: pacnpal/simple-sftp-server:latest user: "${PUID:-1000}:${PGID:-1000}" environment: - SFTP_CHROOT=false
If you run with SFTP_CHROOT=false, this image does not isolate the SFTP user to /home/sftpuser.
SFTP_CHROOT=true and add SYS_CHROOT.Only needed if you want to modify the server yourself:
bashgit clone https://github.com/pacnpal/simple-sftp-server.git cd simple-sftp-server docker build -t simple-sftp-server .
Then use simple-sftp-server instead of pacnpal/simple-sftp-server in the commands above.
user: "${PUID}:${PGID}" / --user "${PUID}:${PGID}"PUID/PGID must match container runtime uid/gidSFTP_CHROOT=true)cap_add: [SYS_CHROOT] / --cap-add SYS_CHROOTMIT — see https://github.com/pacnpal/simple-sftp-server/blob/main/LICENSE.
您可以使用以下命令拉取该镜像。请将 <标签> 替换为具体的标签版本。如需查看所有可用标签版本,请访问 标签列表页面。
来自真实用户的反馈,见证轩辕镜像的优质服务