ProjectSend 2.8.0 Docker Compose 部署指南

在 Ubuntu 24.04 上用官方镜像 projectsend/projectsend:2.8.0 与 Docker Compose 从零部署 ProjectSend:三服务编排、首启初始化、健康检查与登录验证、数据持久化备份、等价 docker run 与七条实测排障 FAQ。

ProjectSendProjectSendDockerDocker ComposeMySQLRedis2026/11/01

ProjectSend 2.8.0 Docker Compose 部署指南

目标:在一台 Ubuntu 24.04 服务器上,用官方镜像 projectsend/projectsend:2.8.0 与 Docker Compose 从零部署一套可登录、可上传、可下载的 ProjectSend 客户文件投递系统,并完成数据持久化验证与首次部署排障。全部命令与回显均在 Ubuntu 24.04.5 LTS(x86_64)+ Docker Engine 29.9.2 + Compose v5.6.1 环境实测通过。

一、环境信息

本节记录实测环境的软硬件基线,读者环境一致即可逐条复现;有差异的项在对应步骤会标注影响。

  • 操作系统:Ubuntu 24.04.5 LTS(x86_64)
  • Docker Engine:29.9.2(docker version 实测)
  • Docker Compose:v5.6.1(docker compose version 实测,插件形态)
  • ProjectSend:2.8.0(官方镜像 projectsend/projectsend:2.8.0,撰写时官方最新稳定版,发布于 2026-10-29)
  • 配套组件:MySQL 8.4(官方模板指定)、Redis 7(官方模板 redis:7-alpine)
  • Web 端口:8135(映射 app 的 80 端口)
  • 部署目录:/srv/projectsend(compose 工作目录,数据均以 bind mount 落在此目录下)
  • 服务器内网地址:192.168.1.172(APP_URL 使用)
  • 镜像体积实测:projectsend/projectsend:2.8.0 约 399MB

版本说明:ProjectSend 2.x 迭代节奏约每周一版(2026-10-29 发布 2.8.0),生产环境建议在 compose 中锁定 2.8.0 这类具体版本号而非浮动 tag,升级时再显式改动。

二、镜像选型说明

本节说明为什么选这三个镜像、官方镜像里有什么,避免"拿来就跑"之后对内部结构一无所知。

镜像版本角色选型依据
projectsend/projectsend2.8.0应用本体官方 Docker Hub 镜像,README 推荐路径;镜像描述"Client file sharing, self-hosted. Community edition."
mysql:8.48.4业务数据库官方 compose 模板 docker/production/compose.example.yaml 指定
redis:7-alpine7.x会话/缓存/队列官方 compose 模板指定;也可不装 Redis,改用 database 驱动(见第七章环境变量表)

官方镜像内部结构(首次启动日志实测佐证):镜像内置 nginx(Web 前端)、PHP-FPM(PHP 8.4 运行时)、supervisord(进程管理),并由 supervisord 拉起 6 组进程:php-fpm、nginx、queue(默认队列)、queue-zips(打包下载队列)、queue-scans(病毒扫描队列)、scheduler(定时调度器)。也就是说一个 app 服务 = Web + 后台队列 + 定时任务,不需要额外为 worker/cron 单独起服务;zip 安装形态才需要按 INSTALL.md 手工配置 worker 与 cron。

镜像 tag 选择:projectsend/projectsend:2 为官方模板使用的跟随大版本浮动 tag,2.8.0 为锁定版本。生产建议锁定完整版本号,升级窗口内再显式变更并回读发行说明。

三、前置条件

本节列出部署前必须就绪的三项,缺一项部署都会在对应环节失败。

  1. Docker Engine 与 Compose 插件已安装且当前用户可执行(官方 README 特别提醒:把用户加入 docker 组后需重新登录,否则所有 docker compose 命令报 permission denied):
docker version && docker compose version

实测回显(节选):

Server: Docker Engine 29.9.2
Docker Compose version v5.6.1
  1. 端口 8135 空闲(如被占用换任意空闲端口,并同步修改 APP_URL,见第十章 FAQ):
ss -ltn | grep 8135 || echo "8135 空闲"

实测回显:

8135 空闲
  1. 磁盘空间:MySQL 数据 + 上传文件存储,建议预留 10GB 以上;若打算启用官方 ClamAV 病毒扫描服务,官方模板注释要求额外预算 1–1.5GB 内存(病毒特征库常驻)。

四、部署步骤

本节是主线:建目录 → 写 compose.yaml → 启动 → 观察首次初始化 → 验证。每一步都有实测回显。

4.1 创建部署目录

mkdir -p /srv/projectsend && cd /srv/projectsend

目录规划:compose.yaml 放在 /srv/projectsend 根下,数据子目录 storage/、db/、redis/ 由下面的 compose.yaml 以 bind mount 方式挂载,便于直接备份(与官方模板的 named volume 方案二选一,差异见第五章)。

4.2 编写 compose.yaml

在 /srv/projectsend 下创建 compose.yaml,内容如下(与官方模板同构:app + db + redis 三服务、数据库健康检查先行、首次启动自动建管理员;持久化方式改为 bind mount 以便备份):

name: projectsend-2.8.0-stack

services:
  app:
    image: projectsend/projectsend:2.8.0
    restart: unless-stopped
    ports:
      - "8135:80"
    environment:
      APP_URL: http://192.168.1.172:8135
      APP_ENV: production
      APP_DEBUG: "false"
      DB_CONNECTION: mysql
      DB_HOST: db
      DB_PORT: "3306"
      DB_DATABASE: projectsend
      DB_USERNAME: projectsend
      DB_PASSWORD: q1w2e3r4
      REDIS_HOST: redis
      CACHE_STORE: redis
      SESSION_DRIVER: redis
      QUEUE_CONNECTION: redis
      ADMIN_NAME: admin
      ADMIN_EMAIL: admin@example.com
      ADMIN_PASSWORD: Q1w2E3r4-doc-2026
    volumes:
      - ./storage:/var/www/html/storage
    depends_on:
      db:
        condition: service_healthy
  db:
    image: mysql:8.4
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: projectsend
      MYSQL_USER: projectsend
      MYSQL_PASSWORD: q1w2e3r4
      MYSQL_ROOT_PASSWORD: q1w2e3r4
    volumes:
      - ./db:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "--silent"]
      interval: 5s
      timeout: 5s
      retries: 20
  redis:
    image: redis:7-alpine
    restart: unless-stopped
    volumes:
      - ./redis:/data

配置逐项说明:

  • name: projectsend-2.8.0-stack:显式项目名,多套自托管服务共存于同一台主机时避免项目名冲突导致误操作他组容器;
  • depends_on: condition: service_healthy:app 会等 db 通过健康检查后再启动并迁移建表,避免竞态;
  • ADMIN_EMAIL / ADMIN_PASSWORD:首次启动自动创建管理员(--if-none 语义,已有任何账号时忽略);不设置则首次访问 Web 时走安装向导;
  • 两个密码注意:管理员口令写了 16 位测试口令而非 8 位短口令——2.8.0 强制管理员口令 ≥12 字符且不得命中公开泄露库(本次实测踩过的坑,见第十一章 FAQ 前两条);数据库口令无此策略,8 位测试口令即可;
  • ./storage bind mount:上传文件、APP_KEY、持久化 .env 全在这一个目录,是备份的关键(见第五章)。

⚠️ 注意:生产环境如需 HTTPS 或置于反向代理之后,端口绑定应改为官方模板的 127.0.0.1:8080:80 回环绑定并配合 TRUSTED_PROXIES 一起修改,两者必须成对调整(官方模板注释强调:单改其一,访客可伪造来源 IP 绕过登录限速并污染下载日志)。参数含义见《ProjectSend 2.8.0 配置参数详解》第 TRUSTED_PROXIES 小节。

4.3 启动并观察首次初始化

docker compose up -d

实测回显(镜像已在本地时;首次会先逐个拉取镜像):

[+] Running 4/4
 ✔ Network projectsend-2.8.0-stack_default  Created
 ✔ Container projectsend-280-stack-redis-1  Started
 ✔ Container projectsend-280-stack-db-1     Started
 ✔ Container projectsend-280-stack-app-1    Started

💡 说明:compose 项目名中的 . 会被清洗,容器名形如 projectsend-280-stack-app-1 属正常现象。

首次初始化约需 30–60 秒(等数据库就绪 → 建表迁移 → 种子数据 → 清缓存 → 拉起后台进程),期间实时查看初始化日志:

docker compose logs -f app

实测回显(首启关键行,节选;全新数据目录时可见 APP_KEY 生成与迁移过程):

projectsend: generating APP_KEY (first boot, persisted to the storage volume)
  2026_07_24_090000_create_roles_tables ......................... 74.66ms DONE
  2026_07_28_120000_add_client_scoped_to_roles_table ............. 8.53ms DONE
  2026_09_13_100000_add_start_page_to_roles_and_users ........... 33.29ms DONE
System roles are in place.
Re-applied 2.8.0.
[01-Nov-2026 03:08:08] NOTICE: fpm is running, pid 36

⚠️ 注意:若 db 尚未就绪,日志会出现 projectsend: waiting for the database... 数行,这是官方 entrypoint 的预期行为(最多等待 60 秒)而非故障;只有等待超时后的报错才需要排查。

4.4 验证部署结果

第一步:三个服务状态与健康检查:

docker compose ps

实测回显:

NAME                            IMAGE                           COMMAND                  SERVICE   CREATED          STATUS                    PORTS
projectsend-280-stack-app-1     projectsend/projectsend:2.8.0   "projectsend-entrypo…"   app       10 minutes ago   Up 10 minutes (healthy)   9000/tcp, 0.0.0.0:8135->80/tcp, [::]:8135->80/tcp
projectsend-280-stack-db-1      mysql:8.4                       "docker-entrypoint.s…"   db        15 minutes ago   Up 15 minutes (healthy)   3306/tcp, 33060/tcp
projectsend-280-stack-redis-1   redis:7-alpine                  "docker-entrypoint.s…"   redis     15 minutes ago   Up 15 minutes             6379/tcp

app 的 (healthy) 是镜像内置健康检查(探测 http://127.0.0.1/up)通过的结果,等价于"应用可正常响应"。

第二步:HTTP 探活:

curl -sI http://127.0.0.1:8135/login | head -5

实测回显:

HTTP/1.1 200 OK
Server: nginx
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Cache-Control: no-cache, private

第三步:管理员账号入库确认(自动建号是否成功):

docker compose exec db mysql -uroot -pq1w2e3r4 -e "SELECT id, type, name, email, active FROM projectsend.users;"

实测回显:

id	type	name	email	active
1	staff	admin	admin@example.com	1

第四步:浏览器登录:访问 http://192.168.1.172:8135,用 admin@example.com / Q1w2E3r4-doc-2026 登录。前端为 Inertia 单页应用,登录成功后进入仪表盘(文件数/客户数统计、上传下载曲线与最近活动)。

第五步(可选):命令行核验安装状态:

docker compose exec app php artisan projectsend:status

实测回显:

ProjectSend 2.8.0 (community)
Capabilities: users.manage, storage.configure, scanning.connect, email.transport.configure, system.updates, news.configure, scheduler.monitoring, custom_assets.manage, branding.customize, captcha.configure, ai.connector, downloads.zip
Staff seats:  1 of unlimited
Clients:      1 of unlimited
Storage:      48 bytes in 1 files
Build:        v2.8.0
Health:       0 migrations pending, 0 failed jobs, 0 queued
Scanning:     off

该命令同时可用于日常巡检(完整用法见《ProjectSend 2.8.0 常用 CLI 命令与工具详解》)。

五、数据持久化说明

本节说清三类数据分别落在哪里、怎么备份,避免"容器重建后文件全丢"。

数据位置(本文 bind mount 方案)内容丢失影响
应用存储卷/srv/projectsend/storage上传的全部文件(按 app/files/<年>/<月>/ 组织)、持久化 .env 与 APP_KEY、日志、框架缓存文件全丢 + 加密数据不可读(APP_KEY 丢失)
数据库/srv/projectsend/dbMySQL 8.4 数据目录:客户/文件元数据/共享关系/审计日志元数据全丢
Redis/srv/projectsend/redis会话、缓存、队列的瞬时数据可丢弃(重建后重新登录即可)

宿主侧实测(部署并上传一个文件后):

ls /srv/projectsend/storage/

实测回显:

app
framework
logs
.env

其中 storage/.env 保存着首次启动生成的 APP_KEY(实测值形如 APP_KEY=base64:WaqRc0SY…,此处节选)。官方 entrypoint 注释强调:APP_KEY 变化会使所有会话失效、所有加密列永久不可读,因此 storage 目录必须纳入备份。

与官方模板的方案差异:官方 compose.example.yaml 使用 named volume(storage / db-data / redis-data),本文使用 bind mount。两者对容器行为无差别;bind mount 的好处是备份/迁移直接拷目录,named volume 的好处是权限由 Docker 托管。二选一即可,勿混用。

备份与恢复实操:

# 备份:停写后打包存储目录 + 逻辑导出数据库
cd /srv/projectsend
docker compose exec db sh -c 'exec mysqldump -uroot -p"q1w2e3r4" projectsend' > projectsend-db-backup.sql
tar czf projectsend-storage-backup.tgz storage/

实测回显(mysqldump 正常完成无报错;文件头尾与落盘大小如下):

head -3 projectsend-db-backup.sql && tail -1 projectsend-db-backup.sql
-- MySQL dump 10.13  Distrib 8.4.13, for Linux (x86_64)
--
-- Host: localhost    Database: projectsend
-- Dump completed on 2026-11-01  3:12:59
ls -lh projectsend-db-backup.sql projectsend-storage-backup.tgz | awk '{print $5, $9}'
77K projectsend-db-backup.sql
569K projectsend-storage-backup.tgz

恢复:新机上先解包 storage/ 到部署目录,再启动 db 后导入 mysql -uroot -p… projectsend < projectsend-db-backup.sql,最后 docker compose up -d 拉起 app。数据库口令如与备份时不同,需同步修改 storage/.env 中的 DB_PASSWORD。

六、环境变量表

本节汇总 app 服务支持的核心环境变量(来源:官方 .env.example、官方 compose.example.yaml 注释与镜像 entrypoint 行为,取值均为本文实测所用或官方默认)。

变量本文取值说明
APP_URLhttp://192.168.1.172:8135对外访问地址;下载链接与密码重置邮件都用它生成,必须写成用户可达的地址
APP_ENVproduction运行环境,生产固定 production
APP_DEBUGfalse调试开关,生产必须 false,否则异常页泄露路径与配置
APP_KEY首启自动生成加密密钥,持久化在 storage/.env;也可显式注入,但运行中严禁更换
DB_CONNECTION / DB_HOST / DB_PORTmysql / db / 3306数据库连接三件套;HOST 写 compose 服务名
DB_DATABASE / DB_USERNAME / DB_PASSWORDprojectsend / projectsend / 与 db 服务一致库名与账号,需与 db 服务的 MYSQL_* 对应
REDIS_HOSTredisRedis 地址(服务名)
CACHE_STORE / SESSION_DRIVER / QUEUE_CONNECTIONredis / redis / redis缓存/会话/队列驱动;官方 .env.example 默认 database,有 Redis 时三者一并切 redis
ADMIN_NAME / ADMIN_EMAIL / ADMIN_PASSWORDadmin / admin@example.com / 见上文首启自动建管理员;仅在任何账号都不存在时生效(--if-none)
TRUSTED_PROXIES未设(直连)置于代理/CDN 后必须设置,与端口绑定方式成对修改
MAIL_MAILER / MAIL_HOST / MAIL_PORT 等默认邮件兜底配置;官方建议登录后在 System → Settings → Email 配置(带发送测试按钮)
PROJECTSEND_SCANNER_DEFAULT_ADDRESS未设首启写入的病毒扫描地址(如 tcp://clamav:3310),与可选 clamav 服务配套

完整参数语义、默认值与生产配置模板见《ProjectSend 2.8.0 配置参数详解》。

七、配置参数说明

compose.yaml 里的环境变量只是 ProjectSend 配置体系的第一层。2.8.0 的完整配置分三层,排查配置问题时按此定位:

  1. Compose 环境变量(本文第六章):随 docker compose up 注入,Laravel 直接读环境变量,优先级最高;
  2. storage/.env 持久化文件:首次启动从 .env.example 复制生成并持久化 APP_KEY;容器内应用同样读取它,宿主机可直接编辑后 docker compose restart app 生效;
  3. Web 界面系统设置(System → Settings):入库存储于数据库 settings 表(键值结构),覆盖病毒扫描、邮件、存储后端、品牌外观、CAPTCHA 等运行期可调项;启动脚本 projectsend:seed-settings 只向"从未设置过"的键写入环境变量默认值,不会覆盖管理员在界面里的修改。

每层生效范围与典型用法的逐项详解见《ProjectSend 2.8.0 配置参数详解》。

八、配置文件挂载

本节把 storage 挂载卷内部的目录职责讲清楚,方便日常到宿主机直接定位。

宿主路径(/srv/projectsend/storage/ 下)容器内路径职责
.env/var/www/html/storage/.env持久化配置文件(APP_KEY 所在),entrypoint 以软链接方式挂到应用根
app/files//var/www/html/storage/app/files/全部上传文件,按 年/月 目录组织(实测:storage/app/files/2026/11/)
framework/同名会话/缓存/视图编译产物(驱动为 redis 时基本为空)
logs/同名应用日志(LOG_CHANNEL=daily 时按 laravel-YYYY-MM-DD.log 滚动)

验证挂载生效:上传任意文件后,宿主机执行:

find /srv/projectsend/storage/app/files -type f | head -3

实测回显(上传 hello-ps.txt 后):

/srv/projectsend/storage/app/files/2026/11/hello-ps.txt

九、等价 docker run 命令

不用 Compose 时,以下三条 docker run + 一条建网命令可部署等价环境(已实测:启动后同样完成迁移、建号,HTTP 200)。逐条执行:

# 1. 创建内部网络
docker network create --subnet 10.89.63.0/24 projectsend-manual-net

# 2. 启动 MySQL 8.4(等待约 30 秒后再执行第 4 步验证)
docker run -d --name projectsend-2.8.0-manual-db --network projectsend-manual-net \
  -e MYSQL_DATABASE=projectsend \
  -e MYSQL_USER=projectsend \
  -e MYSQL_PASSWORD=q1w2e3r4-doc-2026 \
  -e MYSQL_ROOT_PASSWORD=q1w2e3r4-doc-2026 \
  mysql:8.4

# 3. 启动 Redis 7
docker run -d --name projectsend-2.8.0-manual-redis --network projectsend-manual-net \
  redis:7-alpine

# 4. db 就绪后启动 ProjectSend(-p 8136:80 演示用不同宿主端口)
docker run -d --name projectsend-2.8.0-manual-app --network projectsend-manual-net \
  -p 8136:80 \
  -e APP_URL=http://192.168.1.172:8136 \
  -e APP_ENV=production -e APP_DEBUG=false \
  -e DB_CONNECTION=mysql -e DB_HOST=projectsend-2.8.0-manual-db -e DB_PORT=3306 \
  -e DB_DATABASE=projectsend -e DB_USERNAME=projectsend -e DB_PASSWORD=q1w2e3r4-doc-2026 \
  -e REDIS_HOST=projectsend-2.8.0-manual-redis \
  -e CACHE_STORE=redis -e SESSION_DRIVER=redis -e QUEUE_CONNECTION=redis \
  -e ADMIN_NAME=admin -e ADMIN_EMAIL=admin@example.com -e ADMIN_PASSWORD=Q1w2E3r4-doc-2026 \
  projectsend/projectsend:2.8.0

实测回显要点:第 2 步后约 35 秒 docker exec projectsend-2.8.0-manual-db mysqladmin ping -h 127.0.0.1 --silent 静默通过(db 就绪);第 4 步启动后 55 秒容器 (healthy),curl -L http://127.0.0.1:8136/ 返回 HTTP 200,首启日志同样出现 generating APP_KEY (first boot, persisted to the storage volume) 与迁移 DONE 行。

⚠️ 注意:与 Compose 相比,手工 run 的主要差异是 数据未挂载(app 的上传文件与 APP_KEY 在容器可写层,容器删除即丢)。持久化用途请为 app 增加 -v /srv/projectsend/storage:/var/www/html/storage、为 db 增加 -v /srv/projectsend/db:/var/lib/mysql。

验证完毕后的清理:

docker rm -f projectsend-2.8.0-manual-app projectsend-2.8.0-manual-db projectsend-2.8.0-manual-redis
docker network rm projectsend-manual-net

十、常用运维命令

以下命令均在部署目录 /srv/projectsend 下执行,全部实测。

# 查看服务状态与健康检查
docker compose ps

# 跟踪应用日志(排障第一入口)
docker compose logs -f app

# 重启应用(修改环境变量或 storage/.env 后)
docker compose restart app

# 停止与彻底移除(bind mount 数据保留在宿主目录)
docker compose stop
docker compose down

# 升级版本:改 compose.yaml 中的镜像 tag 后
docker compose pull && docker compose up -d

升级实测说明:docker compose pull 拉取新镜像后 up -d 只重建 app 服务;首次启动时 entrypoint 自动执行 projectsend:update(迁移、角色、缓存对齐),日志出现 Re-applied <新版本号> 即升级完成,无需手工跑迁移。升级前务必先做第五章备份(storage + 数据库),APP_KEY 随 storage 卷保留、不可重新生成。

十一、常见问题(FAQ)

以下均为本次部署实测踩到或验证过的问题,按"现象 → 原因 → 解决"沉淀。

Q1:容器反复重启,日志出现 The password field must be at least 12 characters.

现象:docker compose ps 中 app 显示 Restarting (1),日志在 Re-applied 2.8.0. 之后紧跟该行校验报错,随后退出重启。

原因:2.8.0 的首次建号命令(projectsend:admin)强制管理员口令至少 12 个字符;ADMIN_PASSWORD 设置了 8 位口令(如 q1w2e3r4)时初始化失败,entrypoint 以退出码 1 结束,容器进入重启循环。

解决:把 ADMIN_PASSWORD 改为 ≥12 位口令后 docker compose up -d 重建 app 服务。

Q2:口令已达 12 位仍失败,日志出现 The given password has appeared in a data leak.

原因:建号时还会对用户提交口令做公开泄露库(HIBP)比对,命中已知泄露口令即拒绝(实测 16 位的 q1w2e3r4q1w2e3r4 亦被拒)。

解决:换一个未命中泄露库的口令(如本文的 Q1w2E3r4-doc-2026),重建后日志出现 fpm is running、容器转 healthy 即成功。

Q3:up -d 报 Bind for 0.0.0.0:8135 failed: port is already allocated

原因:宿主 8135 已被其他进程/容器占用。

解决:ss -ltn | grep 8135 确认占用者;换空闲端口并同步修改 APP_URL(它决定下载链接与邮件里的地址),再 up -d。

Q4:app 日志反复出现 php_network_getaddresses: getaddrinfo for redis failed: Try again 后 gave up waiting for the database after 60s.

原因:app 容器未接入 compose 网络(本次实测是中途修改过 compose 项目名后,旧容器与新网络失配,容器网络列表为空),导致服务名 redis/db 均无法解析。

解决:彻底重建——docker compose down && docker compose up -d,让容器与网络按当前 compose.yaml 重新配对。修改项目名、服务名后务必整套 down/up,不要只 restart。

Q5:首次启动很慢,一直 waiting for the database...

原因:MySQL 8.4 冷启动初始化(首次建数据目录)耗时 30 秒以上,app 在等它通过健康检查。

解决:等待即可(entrypoint 最多等 60 秒后重试);只有日志出现具体连接错误(如凭据不对)才需要处理。

Q6:登录页能打开但登录后跳转异常 / 下载日志记录的都是同一个 IP

原因:部署在反向代理或 CDN 之后但未配置 TRUSTED_PROXIES,应用把代理 IP 当成访客 IP:登录限速把全体用户当成一个攻击者,下载日志记错地址。

解决:设置 TRUSTED_PROXIES 为代理的地址/网段;官方模板的 * 取值只允许在"端口仅回环可达、唯有代理能连"的部署形态下使用,两处配置必须成对修改。

Q7:忘记管理员口令

解决:登录页走密码重置邮件(需已配置邮件);邮件不可用时的兜底见《ProjectSend 2.8.0 常用 CLI 命令与工具详解》的 projectsend:staff-passwords-on 小节(恢复员工口令登录通道)与 projectsend:admin 小节(建补充管理员)。

十二、参考链接

ProjectSend 2.8.0 Docker Compose 部署指南 - Quzuo