Heimdall 2.8.4 常用 CLI 命令与工具详解
Heimdall 2.8.4 命令行运维面实测:artisan 命令矩阵、PDO 直查 SQLite、/config 文件操作、curl 只读 API、s6/ps 进程核查与 cron 计划任务。
Heimdall 2.8.4 常用 CLI 命令与工具详解
目标:系统梳理 Heimdall 2.8.4 的命令行运维面——
artisan命令矩阵、自定义命令register:app、SQLite 直查(环境无 sqlite3 客户端时的 PDO 方案)、HTTP 只读 API 与健康检查、进程与计划任务核查、日志文件面,并附optimize路由缓存实测失败等坑位实录。本文全部命令与回显均在真实部署环境逐条执行取得(部署过程见《Heimdall Docker Compose 部署指南》)。
一、环境信息
| 项目 | 值 | 说明 |
|---|---|---|
| 组件版本 | Heimdall 2.8.4 | settings 表 version 键实测值 |
| 框架版本 | Laravel Framework 13.39.0 | php artisan --version 实测回显 |
| PHP | 8.5.12(cli,NTS) | which php 实测仅 /usr/bin/php |
| 应用根目录 | /app/www | artisan 入口文件所在 |
| 数据根目录 | /config | 数据库、日志、定制配置所在 |
| 数据库 | /config/www/app.sqlite | 实测 /app/www/database/app.sqlite 软链指向此处 |
| 执行账号 | 服务运行账号(实测 abc) | 全部 CLI 以该账号执行(见第五章注意事项) |
| 监听端口 | 80 / 443 | 本文 HTTP 探针一律打本机回环;宿主侧访问端口由部署层映射决定 |
| 目录视角约定 | 全文路径均为服务自身视角 | 若按《Heimdall Docker Compose 部署指南》部署,/config 对应宿主挂载的 config 目录;/app/www 为运行环境内置的应用代码目录 |
二、工具概览
Heimdall 没有独立的管理 CLI,运维命令行由六个工具面拼成:
| 工具面 | 入口 | 用途 |
|---|---|---|
| artisan | php artisan(位于 /app/www) | 框架命令 + 唯一自定义命令 register:app |
| 数据库直查 | php -r + PDO | 无 sqlite3 客户端时的读库取证 |
| 文件操作 | /config、/app/www | 配置、证书、日志、上传资源 |
| HTTP 只读 API | /health、/api/item | 监控探针与卡片清单拉取 |
| 进程/调度核查 | ps、s6 服务状态、busybox crond | 存活与组件健康 |
| 日志 | /config/log/、/config/www/logs/ | nginx/php/应用三路排障 |
三、前置条件
- 已按《Heimdall Docker Compose 部署指南》完成部署且首启安装(迁移、密钥生成)完成。
- 可进入运行环境的 shell,并切换到服务运行账号(实测
abc)执行命令;以其他账号跑artisan会污染数据文件属主,故障形态与修复见部署篇 FAQ 11.3。 - 工作目录约定:
php artisan ...默认在/app/www下执行;HTTP 探针使用本机回环地址。
四、各工具详解
4.1 artisan 基础面
版本与命令清单:
$ php artisan --version
Laravel Framework 13.39.0
php artisan list 输出框架全量命令,Heimdall 的自定义面只有一个 register 命名空间(实测节选):
register
register:app Add a local app to the registry
其余与运维直接相关的内置命令:about、env、migrate、route:list、config:cache/clear、cache:clear、optimize、down/up、tinker、db、reload(Reload running services)。
about——运行态总览(实测回显全文见配置篇 3.3 节),要点:Environment 区块给出框架/PHP/Composer 版本、Environment local、Debug Mode OFF;Cache 区块给出四类缓存态;Drivers 区块给出 sqlite/file/daily/sync 的驱动选型。
env——环境核对:
$ php artisan env
INFO The application environment is [local].
migrate --status——迁移进度(实测末五条):
2022_03_16_181343_add_app_description_to_items ..................... [1] Ran
2023_01_27_121000_add_role_to_item ................................. [1] Ran
2024_02_16_000000_rename_password_resets_table ..................... [1] Ran
2025_07_17_134226_create_cache_table ............................... [1] Ran
2026_07_09_120000_reassign_orphaned_items .......................... [1] Ran
migrations 表实测共 20 条、batch 均为 1——即首启一次性跑完全部迁移,升级版本时再补增量。日常运维不需要手动跑 migrate(升级流程自动执行),但 --status 是核对"库结构是否跟上了程序版本"的快捷手段。
down / up:进入/退出维护模式(about 回显中的 Maintenance Mode OFF 即其状态位)。改库、换证书等有风险动作前可先 php artisan down。
4.2 缓存类命令与 optimize 实测坑(重点)
常规三件套实测回显:
$ php artisan config:clear
INFO Configuration cache cleared successfully.
$ php artisan cache:clear
INFO Application cache cleared successfully.
config:cache 可单独使用(提速配置加载),但注意它会把 .env 固化——改 .env 后不 config:clear 就不生效,机制与恢复见配置篇 FAQ 7.1。
php artisan optimize 在 2.8.4 上实测失败,回显逐行照抄:
$ php artisan optimize
INFO Caching framework bootstrap, configuration, and metadata.
config ............................................................. 1s DONE
events ......................................................... 0.79ms DONE
routes ............................................................. 1s FAIL
LogicException
Unable to prepare route [tag/{slug}] for serialization. Another route has already been assigned name [tags.show].
(异常栈顶指向 /app/www/vendor/laravel/framework/src/Illuminate/Routing/AbstractRouteCollection.php:257,命令入口 /app/www/artisan:13。)
根因可在路由定义里看到:/app/www/routes/web.php 中 Route::resource('tags', TagController::class)(第 47 行)已生成 tags.show 命名,而第 50 行的手写分组路由 Route::get('/{slug}', [TagController::class, 'show'])->name('show') 又产生同名 tags.show,路由缓存序列化时命名冲突直接抛异常。
失败后的残留态(php artisan about 的 Cache 区块实测):
Config .............................................................. CACHED
Events .............................................................. CACHED
Routes .......................................................... NOT CACHED
Views ............................................................... CACHED
即 optimize 半途而废:config/events/views 已缓存、routes 没缓存。恢复实测三连:
$ php artisan config:clear && php artisan route:clear && php artisan optimize:clear
INFO Configuration cache cleared successfully.
INFO Route cache cleared successfully.
INFO Clearing cached bootstrap files.
config ......................................................... 0.57ms DONE
cache .......................................................... 1.05ms DONE
compiled ....................................................... 0.52ms DONE
events ......................................................... 0.31ms DONE
routes ......................................................... 0.24ms DONE
views .......................................................... 2.22ms DONE
清理后健康端点恢复实测:{"status":"ok","items":1,"users":1}。结论:2.8.4 不要用 optimize 与 route:cache,需要提速只用 config:cache 与 view:cache。
4.3 register:app——注册自定义应用到应用表
这是 Heimdall 唯一的自定义 artisan 命令,作用是把本地应用(图标/配色/API 增强定义)写入支持应用注册表。命令签名实测于源码 /app/www/app/Console/Commands/RegisterApp.php:
protected $signature = 'register:app {folder} {--remove}';
protected $description = 'Add a local app to the registry';
--help 实测全文(节选核心段):
Description:
Add a local app to the registry
Usage:
register:app [options] [--] <folder>
Arguments:
folder
Options:
--remove
行为逻辑(源码实测):参数 folder 传 all 时扫描 app/SupportedApps/ 下全部子目录逐个注册;否则注册单个目录,读取该目录的 app.json 作为注册数据;--remove 反向移除。用法示例:
$ php artisan register:app MyLocalApp
$ php artisan register:app all
$ php artisan register:app MyLocalApp --remove
注册后在 Web 添加卡片时按标题即可命中该应用(自动图标/配色),机制背景见《Heimdall 介绍与选型指南》 3.3 节。
4.4 SQLite 直查(PDO 方案)
先核实工具可用性——实测环境没有 sqlite3 客户端:
$ which sqlite3 php
/usr/bin/php
(sqlite3 无输出,仅 PHP 可用;php -m 实测在位 pdo_sqlite。)因此读库统一走 php -r + PDO。
表清单实测(15 张):
applications
cache
cache_locks
failed_jobs
item_tag
items
jobs
migrations
password_reset_tokens
setting_groups
setting_user
settings
sqlite_sequence
users
注意没有独立的 tags 表——标签也是 items 行(type=1),实测 item_tag 中间表的两个外键都指向 items(id):
CREATE TABLE "item_tag" ("item_id" integer not null, "tag_id" integer not null, "created_at" datetime, "updated_at" datetime, foreign key("item_id") references "items"("id") on delete cascade, foreign key("tag_id") references "items"("id") on delete cascade)
核心表结构实测(节选):
CREATE TABLE "items" ("id" integer primary key autoincrement not null, "title" varchar not null, "colour" varchar, "icon" varchar, "url" varchar not null, "description" text, "pinned" tinyint(1) not null default '0', "order" integer not null default '0', "deleted_at" datetime, "created_at" datetime, "updated_at" datetime, "type" integer not null default '0', "user_id" integer not null default '1', "class" varchar, "appid" varchar, "appdescription" text, "role" text)
CREATE TABLE "settings" ("id" integer primary key autoincrement not null, "group_id" integer not null default '0', "key" varchar not null, "type" varchar not null default 'text', "options" text, "label" varchar not null, "value" varchar, "order" varchar not null default '0', "system" tinyint(1) not null default '0')
CREATE TABLE "users" ("id" integer primary key autoincrement not null, "username" varchar not null, "email" varchar not null, "avatar" varchar, "password" varchar, "autologin" varchar, "public_front" tinyint(1) not null default '0', "remember_token" varchar, "created_at" datetime, "updated_at" datetime)
items.type 的取值语义实测于模型作用域:link→0、tag→1——这就是 API 建卡必须传 type=0 的底层原因(故障实录见部署篇 FAQ 11.4)。
查询示例与实测回显(PDO 逐行打印 JSON):
$ php -r '$p=new PDO("sqlite:/config/www/app.sqlite");foreach($p->query("select id,title,url,type from items") as $r){echo json_encode(array_intersect_key($r,array_flip(["id","title","url","type"]))),"\n";}'
{"id":2,"title":"Portainer","url":"http:\/\/10.89.62.1:9443","type":0}
settings 表按键取值实测回显(节选):
version=2.8.4
language=en
window_target=heimdall
trianglify_seed=heimdall
treat_tags_as=folders
skip_tls_verification=0
行数基线实测:items=1、item_tag=0、applications=0、users=1、settings=15、migrations=20、jobs=0、failed_jobs=0。
tinker 替代方案与 HOME 坑:php artisan tinker --execute='echo 1;' 是交互式查库的另一条路,但实测当执行账号的 HOME=/root 时报:
<warning> User Notice </warning> Writing to directory /root/.config/psysh is not allowed.
psysh 需要写用户目录。给一个可写 HOME 即可正常:
$ HOME=/tmp php artisan tinker --execute='echo 1;'
1
php artisan db:框架自带的数据库会话命令(Start a new database CLI session,支持 --read/--write 指定连接),实测 --help 可正常输出;但在无 sqlite3 客户端的环境里它同样依赖外部 shell,日常取证仍以 PDO 最直接。
4.5 HTTP 只读 API 与健康检查
路由面实测(php artisan route:list --path=api 节选):
GET|HEAD api/item ............. item.index › ItemRestController@index
POST api/item ............. item.store › ItemRestController@store
GET|HEAD api/item/create .... item.create › ItemRestController@create
GET|HEAD api/item/{item} ........ item.show › ItemRestController@show
PUT|PATCH api/item/{item} .... item.update › ItemRestController@update
DELETE api/item/{item} .. item.destroy › ItemRestController@destroy
GET|HEAD api/item/{item}/edit ... item.edit › ItemRestController@edit
GET|HEAD health ............................ health › HealthController
健康端点(监控首选,实测回显):
$ curl -s http://127.0.0.1/health
{"status":"ok","items":1,"users":1}
items/users 直接给库内计数,一条请求同时验证了进程存活、路由可达、数据库可读三层。
卡片清单(只读):
$ curl -s http://127.0.0.1/api/item -H 'Accept: application/json'
[{"title":"Portainer","colour":"#f0a30a","url":"http:\/\/10.89.62.1:9443","description":null,"appid":null,"appdescription":null,"pinned":0,"pinned_order":0,"tags":[]}]
注意清单按 type=0 过滤——非链接类条目(如标签)不出现在结果里。
单条查询实测 500:GET /api/item/2(id 存在)返回:
{
"message": "Server Error"
}
nginx 访问日志同步印证:10.89.62.1 - - [03/Nov/2026:02:04:33 +0800] "GET /api/item/2 HTTP/1.1" 500 44 "-" "curl/8.5.0"。即 2.8.4 的 API show 端点不可用于自动化,取单条数据请拉全量清单本地过滤,或走 4.4 节 PDO。写操作(建卡表单闭环)见部署篇 4.7 节。
4.6 进程与计划任务核查
进程实况(ps 实测节选照抄):
PID USER COMMAND
1 root /package/admin/s6/command/s6-svscan -d4 -- /run/service
36 root s6-supervise svc-php-fpm
38 root s6-supervise svc-nginx
40 root s6-supervise svc-cron
41 root s6-supervise svc-queue
262 abc php /app/www/artisan queue:work database --sleep=3 --tries=3
263 root php-fpm: master process (/etc/php85/php-fpm.conf)
264 root nginx: master process /usr/sbin/nginx -e stderr
265 root busybox crond -f -S -l 5
295 abc nginx: worker process
读法:PID 1 是 s6 监督器,四个 s6-supervise 分别带 php-fpm、nginx、cron、queue 四个服务;queue:work database --sleep=3 --tries=3 是常驻队列消费进程(以服务账号 abc 跑);nginx worker 同样降权到 abc。
服务状态(s6 实测回显):
up (pid 264 pgid 264) 4557 seconds
up (pid 263 pgid 263) 4557 seconds
up (pid 262 pgid 262) 4557 seconds
up (pid 265 pgid 265) 4557 seconds
四路全 up,秒数为持续在线时长;任何一路掉线这里会显示 down/状态变化,是比 ps 更准的存活判据。
计划任务实况:应用层实测没有任何 Laravel 定时任务:
$ php artisan schedule:list
INFO No scheduled tasks have been defined.
系统层 crontab(root)只有周期性维护框架:
# do daily/weekly/monthly maintenance
# min hour day month weekday command
*/15 * * * * run-parts /etc/periodic/15min
0 * * * * run-parts /etc/periodic/hourly
0 2 * * * run-parts /etc/periodic/daily
0 3 * * 6 run-parts /etc/periodic/weekly
0 5 1 * * run-parts /etc/periodic/monthly
实测 /etc/periodic/15min、/etc/periodic/hourly 目录为空——即当前没有任何周期任务实际执行,日志轮转等由 logrotate 状态位驱动(见 4.7)。
4.7 日志与文件面
三路日志落点实测:
$ ls /config/log
heimdall
logrotate.status
nginx
php
/config/log/nginx/ 下为 access.log、error.log;应用日志按 LOG_CHANNEL=daily 滚动到 /config/www/logs/(实测文件 laravel-2026-11-02.log)。访问日志样例行(排障时的原始形态):
10.89.62.1 - - [03/Nov/2026:02:04:33 +0800] "GET /api/item/2 HTTP/1.1" 500 44 "-" "curl/8.5.0"
10.89.62.1 - - [03/Nov/2026:02:05:30 +0800] "GET / HTTP/1.1" 200 5547 "-" "curl/8.5.0"
10.89.62.1 - - [03/Nov/2026:02:05:31 +0800] "GET /health HTTP/1.1" 200 46 "-" "curl/8.5.0"
软链机制实测(应用 storage 与数据目录的桥接):
lrwxrwxrwx 1 root root 16 Nov 3 00:47 logs -> /config/www/logs
lrwxrwxrwx 1 root root 32 Nov 3 00:47 searchproviders.yaml -> /config/www/searchproviders.yaml
lrwxrwxrwx 1 root root 22 Nov 3 00:47 app.sqlite -> /config/www/app.sqlite
应用内部路径(storage/logs、database/app.sqlite 等)全部软链回 /config,所以看日志、查库、改搜索配置一律在 /config 一侧操作即可;/app/www 侧只读不改。
五、注意事项
- 一律以服务运行账号执行 CLI(实测
abc)。其他账号(尤其 root)写过的文件会属主漂移,引发 Web 端 500(部署篇 FAQ 11.3 有完整故障与修复实录)。 optimize/route:cache在 2.8.4 不可用(4.2 实测 LogicException),提速只用config:cache+view:cache;且config:cache后改.env不生效的坑见配置篇 FAQ 7.1。- tinker 需要可写 HOME,
abc账号默认 HOME 不可写时 psysh 报错,临时HOME=/tmp规避(4.4)。 - 环境无 sqlite3 客户端,直查一律 PDO;
app.sqlite在服务运行中不要手工写入修改,读可以随意。 /api/item/{id}单条端点实测 500,自动化取数用清单端点或 PDO。- 备份闭环以
/config为全部(数据库、密钥、配置、日志),打包方法见部署篇第五节。
六、常见问题
6.1 php artisan optimize 报 LogicException(tags.show 命名冲突)
现象、根因、恢复三连命令见 4.2 实测实录;恢复后以 php artisan about 确认 Cache 区块回到 NOT CACHED、curl /health 回到 {"status":"ok",...}。
6.2 tinker 报 "Writing to directory /root/.config/psysh is not allowed"
psysh 要写用户 HOME 而当前 HOME 不可写。实测有效解法:HOME=/tmp php artisan tinker --execute='echo 1;' 输出 1。
6.3 想跑 php artisan env:check 报命令不存在
实测回显:
ERROR Command "env:check" is not defined. Did you mean one of these?
⇂ env
⇂ env:decrypt
⇂ env:encrypt
Laravel 13 已无 env:check;核对环境用 php artisan env(4.1),核对生效值用 php artisan about。
6.4 API 建卡成功但首页与清单都不显示
type 传了字符串(如 link)导致入库值 ≠ 0,而清单按 type=0 过滤(4.4 节模型语义)。正确传法与排障全程见部署篇 4.7 与 FAQ 11.4。
6.5 改了配置想确认进程是否用上了新值
按链路逐项核对:php artisan about(环境/驱动/缓存态)→ curl /health(应用+库可读)→ s6 状态四路 up(4.6)→ 对应日志尾无新增报错(4.7)。
七、参考链接
- 源码仓库(artisan 与 register:app 实现):https://github.com/linuxserver/Heimdall
- Laravel 框架命令行文档:https://laravel.com/docs/artisan
- 站内:《Heimdall 介绍与选型指南》、《Heimdall Docker Compose 部署指南》、《Heimdall 2.8.4 配置参数详解》