Ubuntu 22.04 安装 Hi.Events v1.11.2 指南

目标:在 Ubuntu 22.04 服务器上从零安装 Hi.Events v1.11.2 活动票务平台,提供「源码部署(nginx + PHP-FPM + systemd + cron)」与「官方 Docker 镜像」两套方案:方案 A 面向真实服务器运维习惯,逐条命令实测并收录排障实录;方案 B 为快速路径,完整展开

Hi.Events票务自托管2026/10/23

Ubuntu 22.04 安装 Hi.Events v1.11.2 指南

目标:在 Ubuntu 22.04 服务器上从零安装 Hi.Events v1.11.2 活动票务平台,提供「源码部署(nginx + PHP-FPM + systemd + cron)」与「官方 Docker 镜像」两套方案:方案 A 面向真实服务器运维习惯,逐条命令实测并收录排障实录;方案 B 为快速路径,完整展开见部署指南篇章。全部命令与回显均在 Ubuntu 22.04.5 LTS(x86_64)实测通过。

一、环境信息

项目实测值
操作系统Ubuntu 22.04.5 LTS(Jammy Jellyfish,x86_64)
Hi.Events 版本v1.11.2-beta(tag,git commit 2287e88)
PHP8.3.36(cli/fpm,ondrej PPA)
Composer2.10.3
Laravel(框架版本实测)Laravel Framework 12.34.0(php artisan --version)
PostgreSQL14.24(Ubuntu 官方仓库,满足官方 13+ 要求)
Redisserver 6.0.16(Ubuntu 官方仓库)+ phpredis 扩展 6.3.0
Node.js20.20.2(NodeSource 20.x 源)
Yarn1.22.22(corepack)
nginx1.18.0(Ubuntu 官方仓库)
服务端口Web 入口 80(nginx:/api 与 /storage)、前端页面 5678(SSR)、PostgreSQL 5432、Redis 6379
部署目录/opt/web/hi-events(backend/ 与 frontend/ 同仓两目录)

二、前置条件

2.1 系统与权限

  • 一台 Ubuntu 22.04 服务器,root 或具备 sudo 权限的账号;
  • 服务器可访问外网(GitHub 源码、PPA、NodeSource、Composer 包仓库);
  • 内存 ≥ 2GB(前端 yarn build 峰值内存较高,1GB 实例需加 swap)。

2.2 网络与软件源

方案 A 需要两个第三方软件源:

  • ondrej/php PPA:提供 PHP 8.3(Ubuntu 22.04 官方仓库只有 PHP 8.1,不满足 Laravel 12 的 PHP 8.2+ 要求);
  • NodeSource 20.x:提供 Node.js 20(22.04 官方仓库 Node 为 12.x,无法构建前端)。

2.3 端口占用检查

# ss -ltn | grep -E ':(80|5678|5432|6379)\s'

四个端口均应无输出(未被占用)。若 80 被 nginx 默认站点占用可先移除默认站点(见 4.7)。

三、版本选型说明

  • Hi.Events 当前的稳定线为 v1.11.2-beta(GitHub Releases 2026-10-21 发布,v1 线最新补丁);v2 线处于 v2.0.0-rc.3 候选阶段,不建议生产采用。本文以 tag v1.11.2-beta 安装;
  • PHP 必须 8.3.x:Laravel 12 要求 PHP 8.2+,而官方镜像与迁移实测口径为 8.3(镜像内实测 PHP 8.3.36),源码部署对齐 8.3 减少偏差;
  • PostgreSQL 官方唯一正式支持(13+,迁移自动创建 pg_trgm 扩展);Ubuntu 22.04 仓库自带的 14.24 实测满足;
  • Redis 必装:缓存、队列、会话三类驱动均指向 redis;Ubuntu 22.04 仓库自带 6.0.16 实测可用,追求新特性可升级 7.x。

四、方案 A:源码部署(nginx + PHP-FPM + systemd,推荐生产)

4.1 方案说明

以真实服务器形态部署:后端 Laravel 走 PHP-FPM(nginx 反代 /api 与 /storage),前端构建后由 Node.js SSR 进程(5678 端口)提供页面,队列 Worker 与前端 SSR 由 systemd 常驻托管,调度器走 cron 每分钟触发。该形态与官方「Manual Setup」路线一致,便于深度定制。

访问拓扑(实测):用户浏览器访问 http://<服务器IP>:5678 看页面;页面内的 API 调用按 VITE_API_URL_CLIENT 配置直连 http://<服务器IP>/api(nginx 80)。

4.2 安装系统依赖

# apt update
# apt -y install curl git gnupg ca-certificates lsb-release nginx postgresql redis-server

# add-apt-repository ppa:ondrej/php -y
# apt -y install php8.3-fpm php8.3-cli php8.3-pgsql php8.3-redis php8.3-mbstring \
    php8.3-xml php8.3-curl php8.3-zip php8.3-bcmath php8.3-intl php8.3-gd php8.3-opcache

安装 Composer:

# curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# composer -V
Composer version 2.10.3 2026-08-27 13:34:23

安装 Node.js 20 与 Yarn:

# curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
# apt -y install nodejs
# node -v
v20.20.2
# corepack enable
# yarn -v
1.22.22

版本核对(实测回显):

# php -v | head -1
PHP 8.3.36 (cli) (built: Oct 21 2026 14:31:50) (NTS)
# psql --version
psql (PostgreSQL) 14.24 (Ubuntu 14.24-0ubuntu0.22.04.1)
# redis-server --version
Redis server v=6.0.16 sha=00000000:0 malloc=jemalloc-5.2.1 bits=64 build=d6ed09173b889769
# php -m | grep -E 'redis|pgsql|sodium'
pdo_pgsql
pgsql
redis
sodium

注意:php8.3-sodium 包在 PPA 中不存在,但无需安装——sodium 扩展已内置于 php8.3-common(上面 php -m 已可见)。php8.3-redis 是必装项,原因见 7.1 排障实录。

4.3 创建数据库与账号

# sudo -u postgres psql -c "CREATE USER hievents WITH PASSWORD 'q1w2e3r4';"
CREATE ROLE
# sudo -u postgres psql -c "CREATE DATABASE hievents OWNER hievents;"
CREATE DATABASE

4.4 下载源码并安装后端依赖

# git clone --branch v1.11.2-beta https://github.com/HiEventsDev/Hi.Events.git /opt/web/hi-events
# cd /opt/web/hi-events/backend
# composer install --no-dev --optimize-autoloader

composer install 完成后生成 vendor/ 目录。核对关键依赖(composer.json 实测片段):"php": "^8.2"、"laravel/framework": "^12.0";依赖清单中没有 predis,redis 客户端完全依赖系统的 phpredis 扩展(php8.3-redis)。

4.5 生成密钥与配置 .env

# cd /opt/web/hi-events/backend
# cp .env.example .env
# php artisan key:generate
# php artisan jwt:secret --force

编辑 /opt/web/hi-events/backend/.env 关键键(实测值,10.89.59.10 换成你的服务器 IP):

APP_ENV=production
APP_DEBUG=false
APP_URL=http://10.89.59.10
APP_FRONTEND_URL=http://10.89.59.10:5678
LOG_CHANNEL=daily
DB_CONNECTION=pgsql
DB_HOST=localhost
DB_PORT=5432
DB_DATABASE=hievents
DB_USERNAME=hievents
DB_PASSWORD=q1w2e3r4
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
REDIS_HOST=localhost
REDIS_PORT=6379

两个高频错误:REDIS_HOST 必须是 localhost(本机部署;redis 是 Docker 服务名残留写法);DB_HOST 为 localhost(PostgreSQL 与应用同机)。

4.6 数据库迁移与软链

# php artisan migrate --force
# php artisan storage:link

迁移会自动创建 pg_trgm 扩展(迁移目录内置 extensions.sql:CREATE EXTENSION IF NOT EXISTS pg_trgm;)并建立全部表结构。验证(实测回显):

# php artisan migrate:status | head -8
  Migration name .............................................. Batch / Status
  2020_01_25_113926_initial_db ....................................... [1] Ran
  2024_05_16_222520_update_homepage_background_settings .............. [1] Ran
  2024_06_16_143927_add_locale_to_users .............................. [1] Ran
  2024_06_16_192150_add_locale_to_attendees .......................... [1] Ran
  2024_06_16_192151_add_locale_to_orders ............................. [1] Ran
  2024_07_11_223142_add_description_to_questions_table ............... [1] Ran
# php artisan migrate:status | grep -c Ran
102

目录属主修正(关键步骤):以上 artisan 命令以 root 执行,必须把运行时目录归还给 www-data,否则 FPM 运行期写日志/缓存将失败(真实事故见 7.2):

# chown -R www-data:www-data /opt/web/hi-events/backend/storage /opt/web/hi-events/backend/bootstrap/cache

4.7 配置 nginx 与 PHP-FPM

创建 /etc/nginx/sites-available/hi-events.conf(实测文件原文):

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    root /opt/web/hi-events/backend/public;
    index index.php;
    client_max_body_size 20M;

    location /storage {
        alias /opt/web/hi-events/backend/storage/app/public;
        access_log off;
        expires max;
        add_header Cache-Control "public";
    }

    location ^~ /api/ {
        rewrite ^/api(/.*)$ /index.php$1 break;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/index.php;
        fastcgi_param REQUEST_URI $1;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    location / {
        return 404;
    }
}

要点:nginx 只承接 /api(rewrite 剥掉前缀转 index.php)与 /storage(静态资源别名),页面由 SSR 进程提供;若 80 已被默认站点占用,删除 /etc/nginx/sites-enabled/default 再启用本配置。

# ln -s /etc/nginx/sites-available/hi-events.conf /etc/nginx/sites-enabled/
# rm -f /etc/nginx/sites-enabled/default
# nginx -t && systemctl reload nginx

4.8 构建前端与 SSR

# cd /opt/web/hi-events/frontend
# cp .env.example .env

编辑 /opt/web/hi-events/frontend/.env(实测值):

VITE_API_URL_CLIENT=http://10.89.59.10/api
VITE_API_URL_SERVER=http://10.89.59.10/api
VITE_FRONTEND_URL=http://10.89.59.10:5678

安装依赖并构建(构建脚本依次执行文案抽取/编译、SSR 客户端与服务端构建,产物落在 dist/client 与 dist/server):

# yarn install
# yarn build

构建完成后 server.js(前端目录根)即为 SSR 入口,默认监听 5678。SSR 进程对任意路径都会渲染页面壳(实测对 /api/... 路径同样返回 HTML 页面而非 JSON),因此 API 请求必须走 nginx 80,.env 中两个 VITE_API_URL_* 指向 http://<服务器IP>/api 即是此意。

4.9 systemd 托管队列 Worker 与前端 SSR

创建 /etc/systemd/system/hievents-queue.service(实测文件原文):

[Unit]
Description=Hi.Events queue worker
After=network.target redis-server.service postgresql.service

[Service]
User=www-data
Group=www-data
Restart=always
RestartSec=3
WorkingDirectory=/opt/web/hi-events/backend
ExecStart=/usr/bin/php8.3 artisan queue:work --queue=default,webhook-queue --sleep=3 --tries=3 --timeout=60

[Install]
WantedBy=multi-user.target

创建 /etc/systemd/system/hievents-frontend.service(实测文件原文):

[Unit]
Description=Hi.Events frontend SSR server
After=network.target

[Service]
User=www-data
Group=www-data
Restart=always
RestartSec=3
WorkingDirectory=/opt/web/hi-events/frontend
Environment=NODE_ENV=production
ExecStart=/usr/bin/node server.js

[Install]
WantedBy=multi-user.target

启用并启动(实测状态):

# systemctl daemon-reload
# systemctl enable --now hievents-queue hievents-frontend
# systemctl status hievents-queue --no-pager | head -8
● hievents-queue.service - Hi.Events queue worker
     Loaded: loaded (/etc/systemd/system/hievents-queue.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2026-10-23 09:49:05 UTC; 18min ago
   Main PID: 20954 (php8.3)
     Tasks: 1 (limit: 94371)
     Memory: 42.5M
        CPU: 370ms
# systemctl status hievents-frontend --no-pager | head -8
● hievents-frontend.service - Hi.Events frontend SSR server
     Loaded: loaded (/etc/systemd/system/hievents-frontend.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2026-10-23 09:49:06 UTC; 18min ago
   Main PID: 20960 (node)
     Tasks: 11 (limit: 94371)
     Memory: 86.7M

队列 Worker 日志出现业务任务执行记录即为正常(实测样例):

Oct 23 10:07:03 php8.3[20954]:   2026-10-23 10:07:03 HiEvents\Jobs\Message\SendScheduledMessagesJob  2.04ms DONE
Oct 23 10:07:03 php8.3[20954]:   2026-10-23 10:07:03 HiEvents\Jobs\Waitlist\ProcessExpiredWaitlistOffersJob  RUNNING

4.10 配置调度器 cron

创建 /etc/cron.d/hievents-scheduler(实测文件原文,每分钟以 www-data 触发 Laravel 调度):

* * * * * www-data cd /opt/web/hi-events/backend && /usr/bin/php8.3 artisan schedule:run >> /dev/null 2>&1

4.11 安装验证

# systemctl is-active nginx php8.3-fpm postgresql redis-server hievents-queue hievents-frontend
active active active active active active

# curl -s -o /dev/null -w '%{http_code}\n' http://localhost:5678/
200

# curl -s -X POST http://localhost/api/auth/login \
    -H 'Accept: application/json' -H 'Content-Type: application/json' \
    -d '{"email":"admin@example.com","password":"q1w2e3r4"}'
{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0L2F1dGgvbG9naW4iLCJpYXQiOjE3OTI3NTAwNzcsImV4cCI6MTc5MzM1NDg3NywibmJmIjoxNzkyNzUwMDc3LCJqdGkiOiJZNnBoR21UTHlBclpBT2ZlIiwic3ViIjoiMSIsInBydiI6ImI1YjFhZjFhZWY1MWUyMmU1Y2JlMzk0YzM3NDU3MjJhYjgwYWQ2Y2MiLCJhY2NvdW50X2lkIjoxLCJyb2xlIjoiQURNSU4ifQ.dTjNLiJeGk2TCFjUSiA6iJunXHjsGc-ADRicTXrymQI","token_type":"bearer","expires_in":604800}

首次使用:浏览器访问 http://<服务器IP>:5678 注册管理员账号,再登录后台创建组织者与活动。注意 curl http://localhost/(nginx 80 根路径)返回 404 属预期——80 只服务 /api 与 /storage,页面在 5678。

日常维护命令建议以 www-data 身份执行,避免再次制造属主事故:

# sudo -u www-data php artisan migrate:status

五、方案 B:官方 Docker 镜像部署(快速路径)

5.1 方案说明

官方 daveearley/hi.events-all-in-one:v1.11.2-beta 镜像把 nginx、PHP-FPM、队列 Worker、调度器、前端 SSR 五进程打包为一体(supervisord 托管),并内置 /startup.sh 自动执行迁移与缓存清理,适合快速起栈与体验验证;生产亦可使用(官方生产推荐分离镜像,见部署指南篇章)。

5.2 部署要点

核心三步(完整展开与持久化/备份/恢复实测见《Hi.Events v1.11.2 Docker Compose 部署指南》):

  1. 生成 APP_KEY 与 JWT_SECRET,准备 .env;
  2. 按官方 docker/all-in-one/docker-compose.yml 拉起 all-in-one + postgres:17-alpine + redis:7-alpine 三服务栈;
  3. 首启自动建表,浏览器访问映射端口注册管理员即可使用。

与方案 A 的差异:方案 B 无需手工处理 PHP 扩展、密钥生成之外的任何系统配置,但运行形态锁定为镜像内编排,深度定制(改 nginx 配置、独立扩缩容)不如方案 A 灵活。

六、方案对比与选择建议

维度方案 A:源码部署方案 B:官方镜像
安装耗时约 30~60 分钟(含前端构建)10 分钟级
依赖管理手工(PHP 8.3 PPA + Node 20 + 扩展 + 数据库)镜像内置
升级方式git 拉新 tag + composer + migrate换镜像 tag 自动迁移
定制能力完全(nginx/PHP/进程编排均可改)受镜像约束
故障面系统依赖链较宽(见 7.1~7.3 实录)集中在镜像与数据卷
适合场景生产长期运行、深度定制、内网环境快速验证、中小规模生产

选择建议:生产环境且团队有 PHP 运维经验选方案 A;快速验证或无 PHP 运维经验选方案 B;方案 B 后续需要深度定制时可平滑切换方案 A(数据用 pg_dump 迁移)。

七、常见问题与排障实录

7.1 实录一:HTTP 500 排障长链(php8.3-redis 缺失 → REDIS_HOST 错值 → 日志权限)

症状:方案 A 安装完成后,访问页面返回 HTTP 500 空白;storage/logs/laravel.log 中出现 Class "cache" does not exist。

排查 1(第一层原因):composer.json 依赖里没有 predis,redis 客户端依赖系统 phpredis 扩展,而安装时漏装了 php8.3-redis,CACHE_DRIVER=redis 无法实例化导致报错。补装:

# apt -y install php8.3-redis
# systemctl reload php8.3-fpm

排查 2(第二层原因):仍 500。检查 .env 发现 REDIS_HOST=redis——这是官方 Docker 栈的服务名写法,本机部署应为 localhost。改为 localhost 后,用 php artisan serve(CLI 内置服务器)验证后端已恢复:未带令牌的 API 请求返回 401 JSON(正常业务响应)。但 FPM 路径依旧 500。

排查 3(进程干扰插曲):怀疑残留的 artisan serve 进程占用干扰,执行 pkill -f 'artisan serve' 时命中了命令自身导致会话被杀(exit 143)。正确写法用正则隔开:

# pkill -f 'artisan\s+serve'

排查 4(真凶定位):CLI 正常而 FPM 异常,指向运行用户差异。在 FPM 环境放置探针脚本直接调用 Laravel Kernel 捕获真实异常,得到:

The stream or file "/opt/web/hi-events/backend/storage/logs/laravel.log"
could not be opened in append mode: Permission denied

根因:4.5~4.6 的全部 artisan 命令均以 root 执行,storage/logs/laravel.log 属主为 root,www-data 启动期写日志失败,异常在框架启动期雪崩为 500(与 redis 无关的独立问题,但被前两层修复掩盖)。

修复:

# chown -R www-data:www-data storage bootstrap/cache

修复后 FPM 立即恢复:未带令牌请求返回 401 JSON,带令牌返回业务数据(见 4.11 验证回显)。

教训沉淀:① php8.3-redis 是必装依赖,漏装的表现是 Class "cache" does not exist;② 本机部署 REDIS_HOST=localhost,redis 是 Docker 服务名;③ artisan 维护命令一律 sudo -u www-data 执行,或收尾统一 chown -R www-data:www-data storage bootstrap/cache;④ CLI 正常而 FPM 异常时,用 FPM 探针脚本看真实异常,不要停在日志表象。

7.2 实录二:hievents-frontend 服务启动失败(5678 被占用)

症状:systemctl status hievents-frontend 显示 failed,日志提示端口 5678 被占用。

定位:ss -ltnp | grep 5678 发现一个手工 nohup node server.js 起的残留进程。

处理:

# pkill -f 'node server[.]js'
# systemctl restart hievents-frontend
# systemctl is-active hievents-frontend
active

教训:手工验证进程先收尾,再交给 systemd 托管;server.js 中的 [.] 写法避免 pkill 自匹配。

7.3 实录三:php8.3-sodium 包不存在

安装依赖时若按 apt install php8.3-sodium 会报 Unable to locate package——ondrej PPA 无此包,sodium 扩展已内置于 php8.3-common(php -m | grep sodium 可验证),无需单独安装。

7.4 其他 FAQ

Q1:yarn build 内存不足被杀:构建峰值内存较高,1GB 实例添加 2GB swap 后重试,或改在构建机构建后同步 dist/ 产物。

Q2:访问 5678 出现页面但登录后 API 报跨域/网络错误:.env 中 VITE_API_URL_CLIENT 必须指向 nginx 80 的 /api(与 APP_URL 同主机),并确认浏览器能直连 80 端口;SSR 端口不代理 API。

Q3:php artisan migrate 报连接拒绝:检查 DB_HOST/DB_PORT/DB_USERNAME/DB_PASSWORD 与 4.3 创建的账号一致;PostgreSQL 默认监听 localhost,本机部署无需改 pg_hba.conf(peer 改 md5 的场景仅出现在跨机连接)。

Q4:队列 Worker 停止后订单邮件不发:订单邮件与 Webhook 走异步队列(default 与 webhook-queue 双队列),hievents-queue 服务必须常驻;Restart=always 已含自动拉起,人工重启用 systemctl restart hievents-queue。

八、卸载与清理

8.1 停用服务与移除配置

# systemctl disable --now hievents-queue hievents-frontend
Removed "/etc/systemd/system/multi-user.target.wants/hievents-queue.service".
Removed "/etc/systemd/system/multi-user.target.wants/hievents-frontend.service".
# rm -f /etc/systemd/system/hievents-queue.service /etc/systemd/system/hievents-frontend.service
# systemctl daemon-reload
# rm -f /etc/cron.d/hievents-scheduler
# rm -f /etc/nginx/sites-enabled/hi-events.conf /etc/nginx/sites-available/hi-events.conf
# nginx -t && systemctl reload nginx

8.2 删除数据库与文件

# sudo -u postgres psql -c 'DROP DATABASE hievents;' -c 'DROP USER hievents;'
DROP DATABASE
DROP ROLE
# rm -rf /opt/web/hi-events

依赖包(PHP 8.3、Node.js、PostgreSQL、Redis、nginx)按需保留或用 apt purge 卸载;若卸载 PostgreSQL 前有备份需求,先用 pg_dump 导出。

九、参考链接