transfer.sh 1.6.1 Docker Compose 部署指南

使用官方 noroot 镜像以 Docker Compose 部署 transfer.sh 1.6.1,覆盖镜像选型与本地构建、首次部署全流程、数据持久化与目录结构、环境变量表、配置文件挂载、等价 docker run、常用运维命令与常见问题排障,全部步骤已在 Ubuntu 24.04 服务器实测通过。

transfer.sh文件分享文件传输Go2026/11/01

transfer.sh 1.6.1 Docker Compose 部署指南

‍‍​​‌​> 目标:在一台 Linux 服务器上用 Docker Compose 把 transfer.sh v1.6.1 跑起来并完成上传/下载验证——覆盖镜像选型(官方 noroot 镜像与本地构建两条路径)、首次部署全流程、数据持久化目录结构、环境变量表、配置文件挂载、等价 docker run 与常用运维命令。全部命令与回显已在下述环境实测通过。

一、环境信息

本文所有步骤在以下环境实测完成:

  • 操作系统:Ubuntu 24.04.5 LTS(x86_64)
  • Docker:Docker version 29.9.2, build a82e528
  • Docker Compose:Docker Compose version v5.6.1
  • 组件版本:transfer.sh v1.6.1(transfersh --version 回显 transfer.sh version v1.6.1)
  • 镜像:transfer.sh:v1.6.1-noroot(内容与官方 dutchcoders/transfer.sh:v1.6.1-noroot 一致,选型说明见第二章)
  • 服务端口:宿主机 8130 → 容器内 8080(容器内端口由镜像 ENTRYPOINT 固定为 8080)
  • 数据目录:项目目录下 compose-data/(bind mount 到容器内 /data)
  • Compose 项目名:transfer-sh-1.6.1-stack(compose 文件中显式 name: 指定)

验证环境版本的命令与实测回显:

docker --version
Docker version 29.9.2, build a82e528
docker compose version
Docker Compose version v5.6.1
grep PRETTY_NAME /etc/os-release; uname -m
PRETTY_NAME="Ubuntu 24.04.5 LTS"
x86_64

transfer.sh 本身是单个静态编译的 Go 二进制(--help 全量参数见《transfer.sh 1.6.1 配置参数详解》第二章),镜像内除二进制外只有 mime.types 与 CA 证书,资源占用极小(实测运行中 RSS 约 25 MB)。

二、镜像选型说明

2.1 官方镜像与标签

transfer.sh 官方在 Docker Hub 发布 dutchcoders/transfer.sh 镜像,同一版本提供两种标签形态(来源:官方仓库 README「Docker」章节,查询日期 2026-11-01):

标签形态示例运行用户说明
latest / latest-norootdutchcoders/transfer.sh:latestroot / 5000指向最新构建,README 明确不建议生产使用(可能包含未发布改动)
vx.y.zdutchcoders/transfer.sh:v1.6.1root正式版本,root 运行
vx.y.z-norootdutchcoders/transfer.sh:v1.6.1-noroot5000(非特权)正式版本 + 非 root 运行,官方推荐形态

关键差异解读:生产部署一律选 v1.6.1-noroot 这类带版本号的 -noroot 标签——版本号保证可复现,-noroot 保证服务进程无 root 权限,即便被攻破也难以直接越权写宿主文件。

官方镜像拉取实测(官方源直连成功,注意回显末尾的 digest):

docker pull dutchcoders/transfer.sh:v1.6.1-noroot
v1.6.1-noroot: Pulling from dutchcoders/transfer.sh
97f5f14b9f64: Pulling fs layer
32b9a28ccef5: Pulling fs layer
df5074319a19: Pulling fs layer
0bd7b686b904: Pulling fs layer
26514c47e9eb: Pulling fs layer
0bd7b686b904: Download complete
32b9a28ccef5: Download complete
df5074319a19: Download complete
26514c47e9eb: Download complete
97f5f14b9f64: Download complete
0bd7b686b904: Pull complete
32b9a28ccef5: Pull complete
df5074319a19: Pull complete
26514c47e9eb: Pull complete
97f5f14b9f64: Pull complete
ec280d9a8bb4: Download complete
Digest: sha256:8db9ade72a0da260f0b6a52897bd1c7c35c3d5f25b3f9f9c7a47c2a354687703
Status: Downloaded newer image for dutchcoders/transfer.sh:v1.6.1-noroot
docker.io/dutchcoders/transfer.sh:v1.6.1-noroot

⚠️ 网络说明:以上为官方 Docker Hub 源直连拉取成功的实测回执(2026-11-01)。另一路经 DaoCloud 公共镜像加速器(docker.m.daocloud.io)的拉取实测失败,报错为「Error response from daemon: error from registry: … 这镜像不在白名单. this image is not in the allowlist.」——该加速器对 transfer.sh 仓库不在白名单内,请直接走官方源。

2.2 镜像内部构成

用 docker inspect 查看官方同名配置(本地构建镜像与官方镜像配置一致):

docker inspect transfer.sh:v1.6.1-noroot \
  --format '{{json .Config.Entrypoint}} | {{json .Config.Cmd}} | {{.Config.User}}'
["/go/bin/transfersh","--listener",":8080"] | null | transfersh

三个关键信息:

  1. ENTRYPOINT 已带 --listener :8080:容器内固定监听 8080,docker run/compose 的 command: 传的是「追加参数」(如 --provider local --basedir /data),不要重复写 --listener,也不要指望默认参数——transfersh 裸启动默认监听 127.0.0.1:8080,在容器内等于没监听;
  2. 运行用户是 transfersh(UID/GID 5000):这就是 -noroot 标签的含义,直接决定了第五章的数据目录属主要求;
  3. 基底为 scratch:镜像内没有 shell、没有包管理器,无法 docker exec 进容器排查,日志一律走 docker compose logs 或服务端 --log 参数(见第十章)。

2.3 本地构建镜像(备选路径)

若需要离线环境部署或想固定二进制来源,可复刻官方 Dockerfile 的 final 阶段本地构建。实测使用的 Dockerfile 全文如下(builder 阶段因构建机访问 Docker Hub 拉取 Go 构建镜像不可用,改用「官方 Releases 静态二进制直供」方案,二进制 sha256 已与官方 transfersh-v1.6.1-linux-amd64 校验值核对一致:46fb5ac519769bae8a626cc3f79e2101013ff13beb75d5a613b8df6970d9e27c):

# 复刻官方 Dockerfile(v1.6.1)final 阶段:scratch + mime.types + CA + 非 root 5000
# builder 阶段因 Docker Hub 不可达改用「官方 Releases 静态二进制」直供(sha256 已对官方校验值核验)
FROM alpine:3 AS assets
RUN apk add -q mailcap
RUN mkdir -p /tmp/empty /tmp/useradd
COPY passwd /tmp/useradd/passwd
COPY group /tmp/useradd/group
FROM scratch AS final
LABEL maintainer="Andrea Spacca <andrea.spacca@gmail.com>"
COPY --from=assets /etc/mime.types /etc/mime.types
COPY --from=assets /tmp/empty /tmp
COPY --from=assets /tmp/useradd/passwd /etc/passwd
COPY --from=assets /tmp/useradd/group /etc/group
COPY --from=assets /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --chown=5000:5000 transfersh-v1.6.1-linux-amd64 /go/bin/transfersh
USER transfersh
ENTRYPOINT ["/go/bin/transfersh", "--listener", ":8080"]
EXPOSE 8080

其中 passwd/group 两个文件内容(非 root 用户定义):

transfersh:x:5000:5000::/nonexistent:/sbin/nologin
transfersh:x:5000:

构建与打标(在与 Dockerfile 同目录、二进制就位后执行):

docker build -t transfer.sh:v1.6.1-noroot .

构建产物大小实测 36 MB(官方镜像同版实测 36.4 MB,差异来自基础资产层打包方式),二进制本体完全一致。本文后续 compose 部署使用该本地镜像标签 transfer.sh:v1.6.1-noroot;使用官方全名标签的读者把 image: 改为 dutchcoders/transfer.sh:v1.6.1-noroot 即可,其余步骤完全相同。

三、前置条件

  1. Docker Engine 与 Docker Compose v2 已安装(版本要求见第一章,docker compose version 能正常回显即可);
  2. 规划宿主机端口:容器内固定 8080,宿主侧端口自选,本文用 8130(确认未占用:ss -ltn | grep 8130 应无输出);
  3. 规划数据目录:本文统一放 /opt/transfer-sh/,上传数据落 compose-data/ 子目录;
  4. 数据目录属主必须是 UID/GID 5000(镜像内服务进程运行用户),否则启动后上传报 Could not save metadata——这是最常见的首跑故障,排障实录见第十一章 11.2;
  5. 防火墙如启用需放行宿主端口(如 ufw allow 8130/tcp)。

准备命令与实测回显:

mkdir -p /opt/transfer-sh/compose-data && cd /opt/transfer-sh
chown -R 5000:5000 compose-data
ls -ln | grep compose-data
drwxr-xr-x 2 5000 5000 4096 Nov  1 14:58 compose-data

⚠️ 注意:chown 5000:5000 不是可有可无。镜像内进程以 UID 5000 运行,若目录属 root(新建目录的默认属主),服务能启动、首页能访问,但任何上传都会失败,错误现象见第十一章 11.2。

四、部署步骤

4.1 编写 compose 文件

在 /opt/transfer-sh/ 下创建 docker-compose.yml,全文如下(与实测运行文件一致):

name: transfer-sh-1.6.1-stack

services:
  app:
    image: transfer.sh:v1.6.1-noroot
    container_name: transfer-sh-1.6.1-web
    command: --provider local --basedir /data
    ports:
      - "8130:8080"
    volumes:
      - ./compose-data:/data
    restart: unless-stopped
    networks:
      ts-net:

networks:
  ts-net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.58.0/24

逐项说明:

  • name: transfer-sh-1.6.1-stack:显式指定 Compose 项目名,生成的网络/容器名都带此前缀,避免多套部署互相干扰,也保证 compose down 只作用于本项目;
  • image:镜像标签说明见第二章;官方镜像用户把值改为 dutchcoders/transfer.sh:v1.6.1-noroot;
  • container_name:固定容器名,便于 docker logs/docker inspect 直达;
  • command: --provider local --basedir /data:追加到 ENTRYPOINT 之后的启动参数,等价于 transfersh --listener :8080 --provider local --basedir /data。--provider local 使用本地磁盘存储(四种后端对比见《transfer.sh 介绍与选型指南》第三章),--basedir /data 指定数据落在挂载目录;
  • ports: "8130:8080":宿主 8130 → 容器 8080;
  • volumes: ./compose-data:/data:数据持久化 bind mount,详见第五章;
  • restart: unless-stopped:宿主机重启后自动拉起(手动 stop 的除外);
  • networks.ts-net:显式创建项目专用 bridge 网络并固定网段 10.89.58.0/24(网段可按自己环境调整,避开既有网络即可)。单机部署其实可以省略 networks 段让 Compose 自建默认网络,显式写出的好处是网段可控、便于与同机其他部署避让。

4.2 启动服务

cd /opt/transfer-sh
docker compose up -d

首次创建的实测回显:

 Container transfer-sh-1.6.1-web Creating
 Container transfer-sh-1.6.1-web Created
 Container transfer-sh-1.6.1-web Starting
 Container transfer-sh-1.6.1-web Started

4.3 确认运行状态

docker compose ps
NAME                    IMAGE                       COMMAND                  SERVICE   CREATED                  STATUS                  PORTS
transfer-sh-1.6.1-web   transfer.sh:v1.6.1-noroot   "/go/bin/transfersh …"   app       Less than a second ago   Up Less than a second   0.0.0.0:8130->8080/tcp, [::]:8130->8080/tcp

再看一眼服务日志确认监听正常:

docker compose logs app

(输出节选,实测前 5 行)

transfer-sh-1.6.1-web  | [transfer.sh]2026/11/01 06:58:39 Transfer.sh server started.
transfer-sh-1.6.1-web  | using temp folder: /tmp/
transfer-sh-1.6.1-web  | using storage provider: local
transfer-sh-1.6.1-web  | [transfer.sh]2026/11/01 06:58:39 starting to listen on: :8080
transfer-sh-1.6.1-web  | [transfer.sh]2026/11/01 06:58:39 ---------------------------

日志里三行关键信息与预期一致:临时目录 /tmp/、存储后端 local、监听 :8080。

4.4 功能验证(上传/下载闭环)

服务端首页在浏览器打开 http://<服务器IP>:8130/ 即可看到说明页;命令行验证走 curl(curl 用法细节见《transfer.sh 1.6.1 curl 上传下载命令详解》):

curl -s --upload-file hello.txt http://192.168.1.172:8130/hello.txt
http://192.168.1.172:8130/WNwFDdYW95/hello.txt

响应即下载 URL。下载回读并比对内容一致性:

diff hello.txt <(curl -s http://192.168.1.172:8130/WNwFDdYW95/hello.txt) && echo "DIFF: identical"
DIFF: identical

再确认首页文本态(curl 默认请求返回纯文本说明页):

curl -s http://192.168.1.172:8130/ | head -3
192.168.1.172:8130: Easy file sharing from the command line
===
made with <3 by DutchCoders

上传、下载、首页三者都正常,部署完成。

五、数据持久化说明

5.1 挂载关系与目录结构

./compose-data:/data 是整个部署唯一的持久化点:--provider local 后端把所有上传数据写进 --basedir(即容器内 /data),对应宿主机 compose-data/。local 后端的落盘结构为「每个上传一个以随机 token 命名的一级子目录,目录内是文件本体 + 同名 .metadata 元数据文件」:

ls -la /opt/transfer-sh/compose-data | head -8
total 36
drwxr-xr-x 9 5000 5000 4096 Nov  1 14:54 .
drwxr-xr-x 4 5000 5000 4096 Nov  1 14:20 ..
drwx------ 2 5000 5000 4096 Nov  1 14:22 N9jgbP8uCl
drwx------ 2 5000 5000 4096 Nov  1 14:22 Ri4QjkDfVE
drwx------ 2 5000 5000 4096 Nov  1 14:22 fYdpkLW5X8
drwx------ 2 5000 5000 4096 Nov  1 14:22 vKNyDlRSUi
drwx------ 2 5000 5000 4096 Nov  1 14:22 xo9KSLWTnj

每个 token 目录内部:

find /opt/transfer-sh/compose-data/N9jgbP8uCl -maxdepth 1
/opt/transfer-sh/compose-data/N9jgbP8uCl
/opt/transfer-sh/compose-data/N9jgbP8uCl/hello.txt.metadata
/opt/transfer-sh/compose-data/N9jgbP8uCl/hello.txt

.metadata 是 JSON 元数据(下载次数计数、过期时间、删除令牌等),内容实测示例:

{"ContentType":"","ContentLength":50,"Downloads":0,"MaxDownloads":-1,"MaxDate":"2026-11-02T06:49:47.761965554Z","DeletionToken":"eR4MJDEL5aY7ElAoVGz0","Encrypted":false,"DecryptedContentType":""}

字段含义:Downloads/MaxDownloads 对应 Max-Downloads 请求头的计数与上限(-1 为不限);MaxDate 对应 Max-Days 的到期时刻(零值 0001-01-01T00:00:00Z 表示永不过期);DeletionToken 是删除令牌(其 URL 形态即响应头 X-Url-Delete);Encrypted 标记是否服务端加密落盘。⚠️ 修改 .metadata 需谨慎:它同时是下载鉴权依据,误改会导致文件 404(实测验证见《transfer.sh 1.6.1 配置参数详解》5.5 节)。

5.2 属主与权限

服务进程 UID/GID 为 5000,token 子目录由服务创建,权限为 drwx------(仅 5000 可读写),文件为 -rw-------。宿主机侧只需保证 compose-data 本身属主为 5000:5000(第三章已 chown),子目录权限服务自己维护,不需要人工干预。

5.3 备份与迁移

数据自包含于一个目录,备份即打包:

tar -czf transfer-sh-data-$(date +%F).tar.gz -C /opt/transfer-sh compose-data

迁移到新服务器:把 tar 包解到新机相同相对结构、chown -R 5000:5000 compose-data、启动 compose——token/元数据/删除令牌全部保留,旧 URL 继续可下载。

六、环境变量表

transfer.sh 的全部参数均支持「flag 传参」与「环境变量传参」两种方式——--help 输出里每个参数末尾的 [$大写名称] 即对应环境变量名(全量参数表见《transfer.sh 1.6.1 配置参数详解》第四章)。环境变量方式已在二进制形态实测生效(PROVIDER=local LISTENER=:8143 RATE_LIMIT=3 TEMP_PATH=/tmp BASEDIR=/opt/transfer.sh/data 启动后限流 429 行为与 flag 方式一致)。常用项与默认值:

参数(flag)环境变量默认值说明
--listenerLISTENER127.0.0.1:8080HTTP 监听地址(镜像 ENTRYPOINT 已固定为 :8080)
--providerPROVIDER无存储后端:local/s3/gdrive/storj,不设置启动即报错
--basedirBASEDIR无local 后端数据目录
--temp-pathTEMP_PATH/tmp临时文件目录
--logLOG/var/log/transfersh.log服务日志文件路径
--max-upload-sizeMAX_UPLOAD_SIZE0(不限)单文件上传上限,单位 KB
--rate-limitRATE_LIMIT0(不限)每分钟请求上限
--purge-daysPURGE_DAYS0(关闭)上传自动清理天数
--purge-intervalPURGE_INTERVAL0(关闭)清理任务执行间隔,单位小时
--random-token-lengthRANDOM_TOKEN_LENGTH10下载 token 长度
--http-auth-user / --http-auth-passHTTP_AUTH_USER / HTTP_AUTH_PASS无HTTP Basic 认证账号/密码
--http-auth-htpasswdHTTP_AUTH_HTPASSWD无htpasswd 文件认证
--http-auth-ip-whitelistHTTP_AUTH_IP_WHITELIST无免认证 IP 白名单
--ip-whitelist / --ip-blacklistIP_WHITELIST / IP_BLACKLIST无访问控制白/黑名单
--cors-domainsCORS_DOMAINS无允许跨域的 Origin 列表
--tls-cert-file / --tls-private-keyTLS_CERT_FILE / TLS_PRIVATE_KEY无TLS 证书与私钥
--lets-encrypt-hostsHOSTS无Let's Encrypt 自动签发域名
--clamav-hostCLAMAV_HOST无ClamAV 扫描服务地址
--virustotal-keyVIRUSTOTAL_KEY无VirusTotal API Key(系统级密钥,用变量注入,勿写进 compose 文件入库)

在 compose 中用环境变量的写法示例(与 command: 传 flag 二选一即可,同时设置时 flag 优先):

services:
  app:
    image: transfer.sh:v1.6.1-noroot
    command: --provider local --basedir /data
    environment:
      RATE_LIMIT: "60"
      MAX_UPLOAD_SIZE: "102400"
      RANDOM_TOKEN_LENGTH: "12"

💡 说明:VIRUSTOTAL_KEY 这类第三方服务密钥属于系统级真实密钥,不要明文写进 compose 文件提交到版本库,用 environment: VIRUSTOTAL_KEY: "${VIRUSTOTAL_KEY}" 形态引用,运行前 export VIRUSTOTAL_KEY=<真实值> 注入。

七、配置参数说明

compose 部署里调参数有两条路:

  1. 改 command:(推荐):追加 flag,例如加上传上限、限流与自动清理:
    command: >-
      --provider local
      --basedir /data
      --max-upload-size 102400
      --rate-limit 60
      --purge-days 30
      --purge-interval 6
      --random-token-length 10
  1. 改 environment::见第六章表。

每个参数的实测行为(413 超限、429 限流、403 黑名单、401 认证、purge 过期 404 等)不在本章展开,统一见《transfer.sh 1.6.1 配置参数详解》。改完参数应用的标准动作:

cd /opt/transfer-sh
docker compose up -d

实测回显(配置变更会触发重建;本例为临时加入 RANDOM_TOKEN_LENGTH 环境变量后执行 docker compose up -d):

 Container transfer-sh-1.6.1-web Recreate
 Container transfer-sh-1.6.1-web Recreated
 Container transfer-sh-1.6.1-web Starting
 Container transfer-sh-1.6.1-web Started

⚠️ 注意:--purge-days 启用后清理任务用定时器驱动,首次执行发生在启动后 --purge-interval 小时(最小粒度小时),不会启动即清理;只对 local 后端生效。

八、配置文件挂载

transfer.sh 没有主配置文件——所有配置经 flag/环境变量传入,不存在「挂载一个 transfer.sh.conf」的操作。实际需要挂载文件的是三类辅助资源:

  1. htpasswd 认证文件(--http-auth-htpasswd 用)。生成 apr1 哈希文件:
printf "admin:%s\n" "$(openssl passwd -apr1 -salt ts161salt q1w2e3r4)" > /opt/transfer-sh/htpasswd
cat /opt/transfer-sh/htpasswd
admin:$apr1$ts161sal$6CNCex5SESDLSuqclsqZ50

compose 中追加挂载与参数:

    volumes:
      - ./compose-data:/data
      - ./htpasswd:/etc/transfer.htpasswd:ro
    command: >-
      --provider local
      --basedir /data
      --http-auth-htpasswd /etc/transfer.htpasswd

启用后未带凭据的上传返回 401 Not authorized,curl -u admin:q1w2e3r4 正常上传,实测回显见《transfer.sh 1.6.1 配置参数详解》5.4 节。

  1. TLS 证书与私钥(--tls-cert-file/--tls-private-key 用):同样 bind mount 进容器后以 flag 引用路径,并按需加 --force-https(HTTP 请求 308 跳转 HTTPS,行为实测见 C1 篇 5.2 节)。

  2. 自定义前端静态文件(--web-path 用):替换内置下载页模板时挂载自定义静态文件目录;绝大多数部署用不到,保持缺省即可。

九、等价 docker run

不使用 compose 的等价单条命令(与第四章 compose 完全等价):

docker run -d \
  --name transfer-sh-1.6.1-standalone \
  -p 8151:8080 \
  -v /opt/transfer-sh/run-data:/data \
  transfer.sh:v1.6.1-noroot \
  --provider local --basedir /data

实测回显(返回容器 ID 长串即启动成功):

247b4799d198161f799cb62a901ab753186f8d4d5b110cda0e79f2b58b883e02

验证(独立数据目录 run-data,端口 8151):

curl -s --upload-file hello.txt http://192.168.1.172:8151/hello.txt
http://192.168.1.172:8151/q4s3xBEqyF/hello.txt
curl -s http://192.168.1.172:8151/q4s3xBEqyF/hello.txt
transfer.sh curl upload test
line2 for range test

compose 与 docker run 的取舍:compose 适合长期部署(参数/挂载/网络声明式管理、up -d 幂等更新),docker run 适合临时验证。生产建议 compose。

十、常用运维命令

操作命令说明
状态查看docker compose ps服务运行状态与端口映射
日志查看docker compose logs --tail 100 app标准输出日志(含每次上传/下载的访问记录)
日志落盘command: 加 --log /data/transfersh.log日志文件随数据目录持久化
重启服务docker compose restart app不重建容器
参数变更生效docker compose up -d配置有变化时自动重建
停止服务docker compose down停容器并删项目网络,数据目录保留
升级版本image: 改为新版本标签后 docker compose up -d数据目录不动,token 全兼容
数据备份tar -czf transfer-sh-data-$(date +%F).tar.gz -C /opt/transfer-sh compose-data见 5.3 节

常用运维命令实测回显汇总:

docker compose restart app
 Container transfer-sh-1.6.1-web Restarting
 Container transfer-sh-1.6.1-web Started
docker compose down
 Container transfer-sh-1.6.1-web Stopping
 Container transfer-sh-1.6.1-web Stopped
 Container transfer-sh-1.6.1-web Removing
 Container transfer-sh-1.6.1-web Removed
 Network transfer-sh-161-stack_ts-net Removing

💡 说明:down 只删除本项目(name: transfer-sh-1.6.1-stack)的容器与网络,compose-data/ 数据目录不受影响;这也是第四章强调显式 name: 的原因——没有项目名时按目录名推断,多套部署容易误伤。

日志落盘方式的实测回显(--log 指向文件后,访问日志包含来源 IP、方法、路径、状态码与客户端 UA):

[transfer.sh]2026/11/01 06:49:47 192.168.1.172:35948 - - [2026-11-01T06:49:47Z] "GET /usvdNeBMoS/expired.txt HTTP/1.1" 404  "" "curl/8.5.0"

十一、常见问题(FAQ)

11.1 启动日志报 bind: address already in use

现象:容器反复重启,日志出现:

[transfer.sh]2026/11/01 06:51:47 starting to listen on: :8135
[transfer.sh]2026/11/01 06:51:47 ---------------------------
[transfer.sh]2026/11/01 06:51:47 listen tcp :8135: bind: address already in use

定位:同一端口上已有实例(本例是同机另一个 transfer.sh 实例占了 8135)。

解决:换端口。compose 部署里容器内固定 8080,冲突通常发生在「宿主端口」层面——把 ports: 左侧换成空闲端口即可;同机跑多个 transfer.sh 二进制实例时改各自 --listener。

11.2 上传报 Could not save metadata(数据目录属主错误)

现象:服务正常启动、首页可访问,但任何上传返回:

Could not save metadata

定位:查看数据目录属主。镜像内进程以 UID 5000 运行,mkdir 新建的目录属 root(UID 0):

drwxr-xr-x 2 0 0 4096 Nov  1 14:58 run-data

进程对目录无写权限,.metadata 落盘失败即报此错。

解决:把数据目录属主改为 5000:5000,无需重启服务:

chown -R 5000:5000 /opt/transfer-sh/run-data
ls -ln | grep run-data
drwxr-xr-x 2 5000 5000 4096 Nov  1 14:58 run-data

修复后重试上传立即成功:

http://192.168.1.172:8151/3V2uRxk344/hello.txt

11.3 用了 latest 标签,行为与文档不符

官方 README 明确不建议生产使用 latest(可能指向未发布的开发构建)。处理:image: 固定为 v1.6.1-noroot 这类版本标签,docker compose up -d 重建生效。

11.4 反向代理后 IP 白名单/认证被绕过(X-Forwarded-For 伪造)

官方 README 顶部保留了安全告警(issue #670):在 IP 过滤与 HTTP 认证场景,攻击者可通过伪造 X-Forwarded-For 请求头绕过检查。本机实测复现:实例配置 --ip-blacklist 拉黑了发起请求的源地址,但带上伪造头后请求被放行:

-- host upload 3KB + X-Forwarded-For: 8.8.8.8 (spoof test) --
HTTP:200

缓解:transfer.sh 暴露在反向代理(nginx/Caddy)之后时,在代理层只注入代理自己生成的 X-Forwarded-For 并丢弃客户端原始值;或暂时不用 IP 白名单形态,改用 --http-auth-* 认证(认证校验不依赖该头)。截至本文查询日(2026-11-01)官方尚未发布包含修复的发行版。

11.5 上传返回 Request Entity Too Large(HTTP 413)

设置了 --max-upload-size(单位 KB)后超过上限的上传被拒。这是预期行为不是故障,调整参数值即可;该参数实测回显见《transfer.sh 1.6.1 配置参数详解》5.3 节。

11.6 配了 --purge-days 但过期文件还在

清理任务按 --purge-interval(小时)定时触发,首次执行在启动后一个间隔才发生;且只对 local 后端生效。到期文件在清理动作发生前仍可下载(下载侧的 Max-Days 到期检查与磁盘清理是两条独立机制,前者即时生效,后者等清理周期)。

十二、参考链接