PicoShare 1.5.4 Docker Compose 部署指南
在 Docker 服务器上用 Compose 部署 PicoShare 1.5.4:镜像选型、compose 编排、登录验证、上传与分享实测、数据持久化与常用运维命令,实测通过。
PicoShare 1.5.4 Docker Compose 部署指南
目标:从零开始在一台 Ubuntu 服务器上用 Docker Compose 部署 PicoShare 1.5.4,完成登录、文件上传、分享链接下载与数据持久化验证。全部命令与回显已在 Ubuntu 22.04 实测通过。
一、环境信息
本文部署过程在以下环境实测:
- 操作系统:Ubuntu 22.04(x86_64)
- Docker 版本:29.9.2(Docker Compose v2 插件随 Docker Engine 提供)
- PicoShare 版本:v1.5.4(官方镜像
mtlynch/picoshare:v1.5.4) - 容器内监听端口:4001;宿主机映射端口:8130
- 数据目录:
/opt/picoshare/data(挂载到服务/data) - 实测容器资源占用:内存 4.09MiB,空闲 CPU 0%
二、镜像选型说明
PicoShare 官方只维护一个镜像仓库 mtlynch/picoshare,无 alpine/slim 等变体之分——镜像本身已基于精简基础层构建(二进制 + litestream 共约 45MB)。选型要点:
- 版本 tag 选择:固定具体版本(如
v1.5.4)而非latest,便于回滚与复现。注意镜像 tag 从 1.5.3 起带v前缀(v1.5.3、v1.5.4),而 1.5.2 及更早版本不带(1.5.2),拉取时需与 Docker Hub 标签逐字一致; - v1.5.4 与旧版差异:v1.5.4 的登录口令环境变量为
PS_SHARED_SECRET,且下载链接路由改为/-<ID>格式(旧版/!<ID>仍兼容保留); - 镜像内置组件:
/app/litestream(S3 兼容数据库备份工具)与/app/docker-entrypoint(入口脚本,按LITESTREAM_BUCKET是否设置决定是否启用备份)。
本文实测拉取(官方源直连可用,未发生降级):
docker pull mtlynch/picoshare:v1.5.4
479840ee4f60: Pull complete
d94c665ce234: Pull complete
Digest: sha256:053fee7cbdc64894e924733d5405158a9baadbecd231bfe97ae69d16fd2c2639
Status: Downloaded newer image for mtlynch/picoshare:v1.5.4
docker.io/mtlynch/picoshare:v1.5.4
💡 技术拓展:文件分享服务的容器镜像来源有两类——官方自建镜像(本文所用)与社区封装镜像(如 linuxserver.io 系列)。官方镜像随源码同源构建、发布节奏一致,但通常不带统一的环境变量再封装;社区镜像常集成更多配置项,但存在版本滞后与维护断档风险。生产环境建议优先官方镜像并固定版本 tag。
三、前置条件
- 服务器已安装 Docker Engine 20.10+ 与 Compose v2 插件:
docker version --format '{{.Server.Version}}'
29.9.2
- 目标端口空闲。本文预分配宿主机端口 8130,部署前实测确认未被占用:
ss -ltnp | grep :8130 || echo "8130 空闲"
8130 空闲
- 数据目录已规划(本文示例为
/opt/picoshare/data)。
四、部署步骤
4.1 编写 docker-compose.yml
创建项目目录并写入编排文件:
mkdir -p /opt/picoshare/compose /opt/picoshare/data
cat > /opt/picoshare/compose/docker-compose.yml <<'EOF'
name: picoshare-1.5.4-stack
services:
picoshare:
image: mtlynch/picoshare:v1.5.4
container_name: picoshare-1.5.4-app
environment:
- PS_SHARED_SECRET=q1w2e3r4
ports:
- "8130:4001"
volumes:
- /opt/picoshare/data:/data
networks:
- picoshare-net
restart: unless-stopped
networks:
picoshare-net:
name: picoshare-1.5.4-net
driver: bridge
ipam:
config:
- subnet: 10.89.58.0/24
EOF
配置逐项说明:
name: picoshare-1.5.4-stack:Compose 项目名,决定容器/网络的归属分组;container_name: picoshare-1.5.4-app:显式容器名,便于docker logs/docker exec直达;PS_SHARED_SECRET=q1w2e3r4:管理员登录口令(v1.5.4 必填项,缺失时服务无法启动);ports "8130:4001":宿主机 8130 → 容器 4001(4001 为 PicoShare 默认监听端口);volumes:宿主机数据目录挂载到容器/data,数据库文件store.db落在宿主机;networks:自定义 bridge 网络,显式指定网段 10.89.58.0/24,避免与宿主机上其他 Compose 项目的默认网段冲突;restart: unless-stopped:异常退出或宿主机重启后自动拉起(手动 stop 的除外)。
4.2 启动服务
cd /opt/picoshare/compose && docker compose up -d
Network picoshare-1.5.4-net Created
Container picoshare-1.5.4-app Created
Container picoshare-1.5.4-app Started
4.3 验证容器与日志
docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
picoshare-1.5.4-app mtlynch/picoshare:v1.5.4 "/app/docker-entrypo…" picoshare 4 seconds ago Up 4 seconds 0.0.0.0:8130->4001/tcp, [::]:8130->4001/tcp
日志确认监听就绪(实测节选):
docker logs picoshare-1.5.4-app 2>&1 | tail -3
Starting without litestream
2026/10/30 09:29:10 /app/cmd/picoshare/main.go:27: starting picoshare server
2026/10/30 09:29:10 /app/store/sqlite/sqlite.go:37: reading DB from /data/store.db
2026/10/30 09:29:10 /app/cmd/picoshare/main.go:70: listening on 4001
⚠️ 注意(实测踩坑):若误用旧教程的
PSK变量名,容器会进入反复重启状态,日志报错failed to read shared secret: PS_SHARED_SECRET or PS_SHARED_SECRET_FILE must be set。v1.5.4 只认PS_SHARED_SECRET(或PS_SHARED_SECRET_FILE指向的口令文件)。
4.4 首页可达验证
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8130/
200
4.5 登录、上传与分享实测
登录获取会话 Cookie(/api/auth 端点,请求体字段为 sharedSecretKey):
curl -si -X POST http://localhost:8130/api/auth -H 'Content-Type: application/json' \
-d '{"sharedSecretKey":"q1w2e3r4"}' | head -3
HTTP/1.1 200 OK
Set-Cookie: sharedSecret=a7XY9QIgm2mimIUtd0ZWY8sETVBgF/ERlYeATvYexqw=; Path=/; Max-Age=2592000; HttpOnly; SameSite=Strict
带 Cookie 上传测试文件(expiration 为必填查询参数,RFC3339 格式且至少晚于当前 1 小时):
echo "hello picoshare" > /tmp/hello.txt
curl -s -b "sharedSecret=a7XY9QIgm2mimIUtd0ZWY8sETVBgF/ERlYeATvYexqw=" \
-F "file=@/tmp/hello.txt" "http://localhost:8130/api/entry?expiration=2026-11-06T00:00:00Z"
{"id":"hKXXo7sX75"}
用返回的 ID 构造分享链接,以匿名身份(不带 Cookie)验证下载:
curl -si http://localhost:8130/-hKXXo7sX75 | head -8
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Disposition: filename="hello.txt"
Content-Length: 27
Content-Security-Policy: sandbox
Content-Type: text/plain
浏览器访问 http://<服务器IP>:8130/,输入口令 q1w2e3r4 登录后即可在 Web 界面上传与复制分享链接。API 与页面的完整用法见 《PicoShare 1.5.4 API 使用详解》。
五、数据持久化说明
PicoShare 把文件内容与全部元数据都存进单个 SQLite 数据库 /data/store.db(容器内路径),因此持久化只需保住这一个目录。实测数据目录内容:
ls -la /opt/picoshare/data/
-rw-r--r-- 1 root root 7921664 Oct 30 17:33 store.db
-rw-r--r-- 1 root root 32768 Oct 30 17:34 store.db-shm
-rw-r--r-- 1 root root 4429032 Oct 30 17:34 store.db-wal
重启容器验证持久化(实测:重启后文件仍可下载):
docker restart picoshare-1.5.4-app >/dev/null && sleep 3
curl -s -o /dev/null -w "重启后下载 -> %{http_code}\n" http://localhost:8130/-hKXXo7sX75
重启后下载 -> 200
💡 技术拓展:SQLite 落盘会产生
store.db(主库)、store.db-wal(预写日志)、store.db-shm(共享内存索引)三个文件。WAL 模式允许读写并发,掉电时主库 + WAL 联合恢复。备份时三者必须一起复制(或使用 Litestream/VACUUM INTO),只拷主库会丢失 WAL 中未合并的数据。
需要异地容灾时,启用镜像内置的 Litestream 复制(S3 兼容存储),在 environment 中追加:
environment:
- PS_SHARED_SECRET=q1w2e3r4
- LITESTREAM_BUCKET=my-backup-bucket
- LITESTREAM_ENDPOINT=s3.example.com
- LITESTREAM_ACCESS_KEY_ID=${PICO_AK}
- LITESTREAM_SECRET_ACCESS_KEY=${PICO_SK}
- LITESTREAM_RETENTION=72h
启用后入口脚本改由 litestream replicate -exec 拉起主进程,WAL 准实时复制到对象存储(快照间隔 24h,保留期默认 72h),并在容器启动时自动执行 litestream restore 恢复已有备份。系统级真实密钥建议用 PS_SHARED_SECRET_FILE 挂载口令文件注入,而非明文写在 compose 中;PICO_AK/PICO_SK 为备份桶密钥,用 shell 环境变量注入(先 export PICO_AK=实际密钥、export PICO_SK=实际密钥,再执行编排重建),避免明文进入编排文件历史。
六、环境变量表
v1.5.4 全部环境变量(来源:官方 README 与源码逐项核对):
| 环境变量 | 作用 | 默认值 | 必填 |
|---|---|---|---|
PS_SHARED_SECRET | 管理员登录口令(与 PS_SHARED_SECRET_FILE 二选一) | 无 | 是(二选一) |
PS_SHARED_SECRET_FILE | 口令文件路径(Docker secrets 场景用) | 无 | 否 |
PORT | HTTP 监听端口 | 4001 | 否 |
PS_BEHIND_PROXY | 置为 true 时按反向代理模式处理日志与生成的链接协议 | 未设置 | 否 |
LITESTREAM_BUCKET | S3 兼容备份桶名;非空即启用 Litestream | 未设置(不启用) | 否 |
LITESTREAM_ENDPOINT | S3 兼容存储 endpoint | 未设置 | 否 |
LITESTREAM_ACCESS_KEY_ID | 备份桶访问密钥 ID | 未设置 | 启用 Litestream 时必填 |
LITESTREAM_SECRET_ACCESS_KEY | 备份桶秘密密钥 | 未设置 | 启用 Litestream 时必填 |
LITESTREAM_RETENTION | 备份快照与 WAL 保留时长 | 72h | 否 |
另有一个启动参数(非环境变量)-db:SQLite 数据库路径,官方镜像默认 data/store.db,Docker 场景由入口脚本固定为 /data/store.db。
七、配置参数说明
PicoShare 没有 YAML/INI 配置文件——所有配置只通过环境变量与启动参数下发,上文两张表即全部可配置项。运行期可在 Web 界面 Settings 页调整「默认文件存活期」(对应 API PUT /api/settings,取值 1~3650 天或永不过期),该值存入数据库、随卷持久化。反向代理场景在 nginx 中把 proxy_pass http://127.0.0.1:8130; 指向本服务,并给容器追加 PS_BEHIND_PROXY=true 即可。
八、配置文件挂载
除数据卷外,PicoShare 唯一可能的文件挂载是口令文件(替代 PS_SHARED_SECRET 明文变量)与Litestream 配置覆盖:
- 口令文件方式(推荐生产使用):
environment:
- PS_SHARED_SECRET_FILE=/run/secrets/picoshare_psk
secrets:
- picoshare_psk
secrets:
picoshare_psk:
file: ./picoshare_psk.txt
- Litestream 的高级配置(快照间隔、压缩等)内置于镜像的
litestream.yml,通过LITESTREAM_*环境变量注入即可覆盖绝大多数需求;如需彻底自定义,可挂载自己的 yml 并绕过官方入口脚本直接运行/app/litestream replicate -exec "/app/picoshare -db /data/store.db"。
九、等价 docker run
与上文 Compose 完全等价的单命令启动方式:
docker run -d \
--name picoshare-1.5.4-app \
--network picoshare-1.5.4-net \
-p 8130:4001 \
-v /opt/picoshare/data:/data \
-e PS_SHARED_SECRET=q1w2e3r4 \
--restart unless-stopped \
mtlynch/picoshare:v1.5.4
参数与 Compose 版逐项对应:-p 端口映射、-v 数据卷、-e 登录口令、--restart 重启策略。日常使用仍建议 Compose 编排,便于升级与网络管理。
十、常用运维命令
以下命令均在实测环境验证(项目目录:/opt/picoshare/compose):
docker compose ps # 查看容器状态
docker logs -f picoshare-1.5.4-app # 跟踪服务日志
docker stats --no-stream picoshare-1.5.4-app # 资源占用
docker compose restart # 重启服务
docker compose down # 停止并移除容器与网络(保留数据卷/目录)
docker compose down -v # 连同命名卷一起移除(本文用宿主机目录挂载,数据不受影响)
docker stats 实测回显:
NAME CPU % MEM USAGE / LIMIT NET I/O BLOCK I/O PIDS
picoshare-1.5.4-app 0.00% 4.09MiB / 15.57GiB 1.2kB / 1.6kB -- / -- 5
版本升级:修改 compose 中镜像 tag(如 v1.5.4 → 新版本)后执行 docker compose up -d,容器以新镜像重建,数据卷不动即完成原地升级;回滚同理改回旧 tag 重建即可。
十一、常见问题
11.1 容器反复重启,日志报 PS_SHARED_SECRET 错误
现象:docker ps 显示 Restarting (1),日志含 failed to read shared secret: PS_SHARED_SECRET or PS_SHARED_SECRET_FILE must be set。
原因:v1.5.4 起登录口令变量为 PS_SHARED_SECRET,旧文档中的 PSK 变量已不被识别。
解决:compose 中改为 PS_SHARED_SECRET=<口令> 后 docker compose up -d 重建。
11.2 上传时报 Invalid expiration URL parameter
原因:expiration 参数不是 RFC3339 格式,或指定的时间距当前不足 1 小时(实测报错原文:Invalid expiration URL parameter: expire time must be at least one hour in the future)。
解决:使用 RFC3339 格式(如 2026-11-06T00:00:00Z),且至少晚于当前 1 小时。注意 v1.5.4 上传 API 的 expiration 是必填参数,省略或传 Never 都会报同样的 400 错误(实测核对过),脚本要近似「不过期」时传一个远期时间即可。
11.3 分享链接 404
排查顺序:确认 URL 格式为 /-<ID>(如 /-hKXXo7sX75,减号开头);旧版 /<ID> 与 /<ID>/<filename> 形式的链接在 v1.5.4 已不再注册路由,/!<ID> 为兼容格式仍可用;再确认文件未被删除或未到过期时间(过期文件由调度器每 7 小时清理一次)。
11.4 端口冲突
宿主机 8130 被占用时,修改 compose 的 ports 左侧端口即可(容器内 4001 不必改动)。检查占用命令:ss -ltnp | grep :8130。