Heimdall 2.8.4 常用 CLI 命令与工具详解

Heimdall 2.8.4 命令行运维面实测:artisan 命令矩阵、PDO 直查 SQLite、/config 文件操作、curl 只读 API、s6/ps 进程核查与 cron 计划任务。

Heimdall面板Docker部署自托管2026/11/03

Heimdall 2.8.4 常用 CLI 命令与工具详解

目标:系统梳理 Heimdall 2.8.4 的命令行运维面——artisan 命令矩阵、自定义命令 register:app、SQLite 直查(环境无 sqlite3 客户端时的 PDO 方案)、HTTP 只读 API 与健康检查、进程与计划任务核查、日志文件面,并附 optimize 路由缓存实测失败等坑位实录。本文全部命令与回显均在真实部署环境逐条执行取得(部署过程见《Heimdall Docker Compose 部署指南》)。

一、环境信息

项目值说明
组件版本Heimdall 2.8.4settings 表 version 键实测值
框架版本Laravel Framework 13.39.0php artisan --version 实测回显
PHP8.5.12(cli,NTS)which php 实测仅 /usr/bin/php
应用根目录/app/wwwartisan 入口文件所在
数据根目录/config数据库、日志、定制配置所在
数据库/config/www/app.sqlite实测 /app/www/database/app.sqlite 软链指向此处
执行账号服务运行账号(实测 abc)全部 CLI 以该账号执行(见第五章注意事项)
监听端口80 / 443本文 HTTP 探针一律打本机回环;宿主侧访问端口由部署层映射决定
目录视角约定全文路径均为服务自身视角若按《Heimdall Docker Compose 部署指南》部署,/config 对应宿主挂载的 config 目录;/app/www 为运行环境内置的应用代码目录

二、工具概览

Heimdall 没有独立的管理 CLI,运维命令行由六个工具面拼成:

工具面入口用途
artisanphp 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/应用三路排障

三、前置条件

  1. 已按《Heimdall Docker Compose 部署指南》完成部署且首启安装(迁移、密钥生成)完成。
  2. 可进入运行环境的 shell,并切换到服务运行账号(实测 abc)执行命令;以其他账号跑 artisan 会污染数据文件属主,故障形态与修复见部署篇 FAQ 11.3。
  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 侧只读不改。

五、注意事项

  1. 一律以服务运行账号执行 CLI(实测 abc)。其他账号(尤其 root)写过的文件会属主漂移,引发 Web 端 500(部署篇 FAQ 11.3 有完整故障与修复实录)。
  2. optimize / route:cache 在 2.8.4 不可用(4.2 实测 LogicException),提速只用 config:cache + view:cache;且 config:cache 后改 .env 不生效的坑见配置篇 FAQ 7.1。
  3. tinker 需要可写 HOME,abc 账号默认 HOME 不可写时 psysh 报错,临时 HOME=/tmp 规避(4.4)。
  4. 环境无 sqlite3 客户端,直查一律 PDO;app.sqlite 在服务运行中不要手工写入修改,读可以随意。
  5. /api/item/{id} 单条端点实测 500,自动化取数用清单端点或 PDO。
  6. 备份闭环以 /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)。

七、参考链接