homepage v2.7.0 日常运维手册

面向已上线的 homepage v2.7.0 单机栈,覆盖日常巡检、配置目录备份与恢复演练、存活探针与登录失败监控、资源水位与日志轮转、版本升级与回滚(v2.6.0↔v2.7.0 双向实测)、Host 校验/只读挂载/端口占用等故障排查的完整运维闭环,全部命令与回显实测沉淀。

homepage自托管仪表盘2026/10/25

homepage v2.7.0 日常运维手册

目标:面向已按部署指南上线的 homepage v2.7.0 单机栈,给出「日常巡检 → 备份与恢复 → 监控告警 → 扩容缩容 → 升级与回滚 → 故障排查」的完整运维闭环,每个操作都附实测命令与真实回显。本文全部命令与回显已在 Docker Engine 29.9.2(Compose v5.6.1)环境实测通过。

一、环境信息

  • 操作系统:Ubuntu 22.04(x86_64),Docker Engine 29.9.2(Compose v5.6.1)
  • homepage 版本:v2.7.0(官方镜像 ghcr.io/gethomepage/homepage,编排名 homepage-v2.7.0-stack)
  • 编排目录:/opt/homepage/(含 docker-compose.yml 与 config/ 配置子目录);备份落盘目录:/var/backups/homepage/
  • 访问形态:http://homepage.example.com:8129(HTTP 直连,已开启密码门控,示例密码 q1w2e3r4);https://homepage.example.com:8130(Caddy HTTPS 反代)
  • 网络规划:独立 bridge 网络 homepage-net(网段 10.89.58.0/24)
  • 登录会话获取:/api/validate 等管理端点需登录态,本文统一用两步 curl(取 CSRF Token → 提交密码)拿到会话 Cookie 后再调用:
cd /opt/homepage
CSRF=$(curl -s -c cookies.txt http://homepage.example.com:8129/api/auth/csrf)
TOKEN=$(echo "$CSRF" | python3 -c 'import json,sys;print(json.load(sys.stdin)["csrfToken"])')
curl -s -b cookies.txt -c cookies.txt -o /dev/null -w "登录回调: HTTP %{http_code}\n" \
  -X POST http://homepage.example.com:8129/api/auth/callback/credentials \
  -d "csrfToken=$TOKEN&password=q1w2e3r4"

实测输出:

登录回调: HTTP 302

💡 说明:homepage v2.0 起所有页面与 /api 路由默认受登录门控保护(免认证的只有探活端点 /api/healthcheck),因此运维脚本凡是调用管理端点都要先带上会话 Cookie。cookies.txt 含有效会话,不要提交进版本库。

二、日常巡检

日常巡检回答三个问题:服务活着吗、跑的是哪个版本、配置还健康吗。本章命令均可直接加入巡检脚本,预期结果逐项给出。

2.1 服务状态与版本巡检

先看服务状态(healthy 即健康检查通过)与当前运行的版本:

cd /opt/homepage
docker compose ps

实测输出:

NAME                  IMAGE                                 COMMAND                  SERVICE    CREATED         STATUS                    PORTS
homepage-v2.7.0-web   ghcr.io/gethomepage/homepage:v2.7.0   "docker-entrypoint.s…"   homepage   5 minutes ago   Up 22 seconds (healthy)   0.0.0.0:8129->3000/tcp, [::]:8129->3000/tcp
docker compose images

实测输出:

CONTAINER             REPOSITORY                     TAG                 PLATFORM            IMAGE ID            SIZE                CREATED
homepage-v2.7.0-web   ghcr.io/gethomepage/homepage   v2.7.0              linux/amd64         19c5c0918445        376MB               36 hours ago

再扫一眼近期日志有没有异常关键字(error/Failed/EROFS 等):

docker compose logs --since 1h homepage

实测输出:

homepage-v2.7.0-web  | /app/.next already owned by correct UID/GID or running as root, skipping chown
homepage-v2.7.0-web  | ▲ Next.js 16.4.1
homepage-v2.7.0-web  | - Local:         http://[::1]:3000
homepage-v2.7.0-web  | - Network:       http://[::]:3000
homepage-v2.7.0-web  | ✓ Ready in 0ms
homepage-v2.7.0-web  | ✓ Running next.config took 2ms

💡 说明:版本以 docker compose images 的 TAG 列为准(当前 v2.7.0,镜像 376MB),日志首行的 ▲ Next.js 16.4.1 是应用本体的框架版本,两者口径不同。巡检基线:healthy + 日志无 error 级输出即为正常。

2.2 应用健康与配置自检

健康检查走免认证探活端点,任何监控工具都能直接用:

curl -s http://homepage.example.com:8129/api/healthcheck

实测输出:

up

配置健康度用自检端点核对(需登录态,会话取法见第一章):

curl -s -b cookies.txt http://homepage.example.com:8129/api/validate

实测输出:

[]

💡 说明:/api/validate 返回全部配置文件的解析错误数组,空数组 [] 即所有 YAML 均可正常解析。homepage 对坏配置的容错是「跳过该文件、页面不崩」,所以配置健康必须靠这个端点而不是页面状态判断(坏 YAML 的完整排障实录见《homepage v2.7.0 配置参数详解》第八章)。

2.3 配置目录与日志体量巡检

homepage 全部持久化状态就是一个配置目录,体量巡检能提前发现异常增长:

du -sh /opt/homepage/config /opt/homepage/config/logs

实测输出:

44K	/opt/homepage/config
8.0K	/opt/homepage/config/logs
ls -lh /opt/homepage/config/logs/

实测输出:

total 4.0K
-rw-r--r-- 1 root root 3.5K Oct 25 11:21 homepage.log

💡 说明:纯配置形态的 homepage 配置目录只有几十 KB 量级(本环境 44K),日志文件单日增长也在 KB 级。若 du 结果出现 MB/GB 级增长,优先排查是否有人把大文件(图标包、附件)放进了挂载目录,或日志因反复报错膨胀(轮转方案见 5.3 节)。

三、备份与恢复

homepage 没有数据库、没有隐藏状态,备份对象就是 /opt/homepage/config 一个目录,恢复也只在该目录上做文章。本章给出手动备份、完整恢复演练与定时备份三件套。

💡 技术拓展:配置目录备份的常见方式对比——

方式原理适用场景
tar 全量打包一次性打包成单文件,便于异地传输与长期归档低频变更的配置类目录(本文采用)
rsync 增量同步按差异同步到备份盘,占用小、可秒级恢复目录形态大目录、高频增量
文件系统快照LVM/ZFS 快照,一致性最好有快照能力且要求时间点一致的存储

homepage 配置目录仅几十 KB 且变更低频,tar 打包最简单可控,本文实际采用 tar + 保留 7 天的定时清理。

3.1 手动备份

mkdir -p /var/backups/homepage
tar czf /var/backups/homepage/homepage-config-20261025-1115.tar.gz -C /opt/homepage config
ls -lh /var/backups/homepage/homepage-config-20261025-1115.tar.gz

实测输出:

-rw-r--r-- 1 root root 2.2K Oct 25 11:19 /var/backups/homepage/homepage-config-20261025-1115.tar.gz

备份前先核对包内容(-C /opt/homepage config 使包内路径统一为 config/,恢复时解到 /opt/homepage 即还原原位):

tar tzf /var/backups/homepage/homepage-config-20261025-1115.tar.gz

实测输出:

config/
config/proxmox.yaml
config/docker.yaml
config/kubernetes.yaml
config/bookmarks.yaml
config/services.yaml
config/secret_key.txt
config/widgets.yaml
config/settings.yaml
config/logs/
config/logs/homepage.log

💡 说明:备份无需停服务——tar 读取的是普通文件,homepage 配置低频变更,读到「半新半旧」的概率可忽略;要求严格一致时可在 docker compose stop 后打包再 start。注意包里除 5 个 YAML 外还有 secret_key.txt(HOMEPAGE_FILE_* 注入的凭据文件)与 logs/,备份该目录即备份全部状态。

3.2 恢复演练(真实故障模拟)

模拟最坏情况——配置目录整体丢失,验证「全新骨架启动 → 备份恢复 → 配置还原」全链路:

第 1 步:停服务,把配置目录挪走模拟丢失:

cd /opt/homepage
docker compose down
mv config config.lost

实测输出:

 Container homepage-v2.7.0-web Stopping 
 Container homepage-v2.7.0-web Stopped 
 Container homepage-v2.7.0-web Removing 
 Container homepage-v2.7.0-web Removed 

第 2 步:空目录启动,观察骨架重建:

mkdir -p config
docker compose up -d

等健康检查转绿后验证——服务正常但已回到官方示例配置(自定义内容全部丢失):

curl -s http://homepage.example.com:8129/api/healthcheck

实测输出:

up
head -3 /opt/homepage/config/settings.yaml

实测输出:

---
# For configuration options and examples, please see:
# https://gethomepage.dev/configs/settings/

⚠️ 注意:骨架重建只会补回官方示例 YAML,自己放进配置目录的自定义文件(如 HOMEPAGE_FILE_* 指向的凭据文件)不会凭空再生——这正是必须备份的原因。

第 3 步:清掉骨架,从备份恢复:

docker compose down
rm -rf /opt/homepage/config
tar xzf /var/backups/homepage/homepage-config-20261025-1115.tar.gz -C /opt/homepage
docker compose up -d

实测输出:

 Container homepage-v2.7.0-web Stopping 
 Container homepage-v2.7.0-web Stopped 
 Container homepage-v2.7.0-web Removing 
 Container homepage-v2.7.0-web Removed 
 Container homepage-v2.7.0-web Starting 
 Container homepage-v2.7.0-web Started 

第 4 步:验证配置原样还原——探针恢复后,用登录会话读取页面标题(该值来自 settings.yaml 里的 {{HOMEPAGE_VAR_DASHNAME}} 占位符渲染):

curl -s http://homepage.example.com:8129/api/healthcheck

实测输出:

up
curl -s -b cookies.txt http://homepage.example.com:8129/ | python3 -c "
import json,sys,re
m=re.search(r'<script id=\"__NEXT_DATA__\" type=\"application/json\">(.*?)</script>', sys.stdin.read(), re.S)
print('title:', json.loads(m.group(1))['props']['pageProps']['initialSettings'].get('title'))
"

实测输出:

title: 测试变量仪表盘

标题与丢失前一致,恢复闭环完成。清理演练残留:rm -rf /opt/homepage/config.lost。

3.3 定时备份

把 3.1 的打包动作固化为脚本 /opt/homepage/backup.sh(打包 → 只保留 7 天内的备份包):

#!/bin/bash
# homepage 配置目录定时备份:打包 + 7 天保留清理
set -e
BACKUP_DIR=/var/backups/homepage
STAMP=$(date +%Y%m%d-%H%M)
mkdir -p "$BACKUP_DIR"
tar czf "$BACKUP_DIR/homepage-config-$STAMP.tar.gz" -C /opt/homepage config
find "$BACKUP_DIR" -name 'homepage-config-*.tar.gz' -mtime +7 -delete
ls -lh "$BACKUP_DIR/homepage-config-$STAMP.tar.gz"

赋予执行权限并手动跑一次验证:

chmod +x /opt/homepage/backup.sh
bash /opt/homepage/backup.sh

实测输出:

-rw-r--r-- 1 root root 2.2K Oct 25 11:24 /var/backups/homepage/homepage-config-20261025-1124.tar.gz

备份目录中按天累积(旧包由脚本按 -mtime +7 自动清理):

ls -lh /var/backups/homepage/

实测输出:

-rw-r--r-- 1 root root 2.2K Oct 25 11:19 homepage-config-20261025-1115.tar.gz
-rw-r--r-- 1 root root 2.2K Oct 25 11:24 homepage-config-20261025-1124.tar.gz

写入 root 的 crontab,每天 03:10 执行(crontab -e 追加一行):

10 3 * * * /opt/homepage/backup.sh >> /var/log/homepage-backup.log 2>&1

⚠️ 注意:备份包只落在本机 /var/backups/homepage/,宿主机磁盘故障时备份会一起丢;生产环境应把该目录再同步到异地(rsync/对象存储均可,包仅 KB 级)。

四、监控告警

homepage 自身不提供指标端点,监控以「存活探针 + 日志事件」两条线组织:探针回答「挂没挂」,日志回答「有没有人撞门」。

💡 技术拓展:服务探针的三种常见方式对比——

方式原理适用场景
HTTP 探针请求专用健康端点,校验状态码与响应体有健康端点的 Web 服务(本文采用)
ICMP 探针ping 主机/IP,只测网络可达不关心应用进程是否存活的粗粒度场景
进程探针检查进程/端口监听存在无健康端点的后台程序

homepage 内置免认证的 /api/healthcheck 端点且返回体可断言(up),HTTP 探针能同时验证「进程在、端口通、应用可用」三层,本文实际采用。

4.1 存活探针

两种访问形态各挂一条 HTTP 探针(HTTPS 自签证书需 -k 跳过校验):

curl -s http://homepage.example.com:8129/api/healthcheck
curl -sk https://homepage.example.com:8130/api/healthcheck

实测输出:

up
up

两条探针分别覆盖「应用本体」与「HTTPS 反代链路」,任一返回非 up(或超时)即触发告警。建议探针间隔 30–60 秒;homepage 无缓存依赖,探针频率对服务端无压力。

4.2 登录失败监控

公网形态下最值得监控的应用层事件是密码撞门。先复现一次失败登录:

CSRF=$(curl -s -c cookies_bad.txt http://homepage.example.com:8129/api/auth/csrf)
TOKEN=$(echo "$CSRF" | python3 -c 'import json,sys;print(json.load(sys.stdin)["csrfToken"])')
curl -s -b cookies_bad.txt -o /dev/null \
  -w "错误密码回调: HTTP %{http_code}, redirect=%{redirect_url}\n" \
  -X POST http://homepage.example.com:8129/api/auth/callback/credentials \
  -d "csrfToken=$TOKEN&password=wrongpass"

实测输出:

错误密码回调: HTTP 302, redirect=http://homepage.example.com:8129/api/auth/error?error=CredentialsSignin&provider=credentials

应用日志留下固定格式的 warn 记录:

cd /opt/homepage
docker compose logs --since 5m homepage | grep -i "sign-in"

实测输出:

homepage-v2.7.0-web  | [2026-10-25T03:21:03.361Z] warn: <nextauth> Failed password sign-in attempt

Failed password sign-in attempt 这一行就是监控与封禁的锚点:最小方案是定时 grep 该关键字超阈值即告警;正式方案是对接 fail2ban/CrowdSec,按源 IP 对 /api/auth/callback/credentials 的失败 POST 自动封禁(homepage 应用层不做限速,公网部署必须补上这层)。

五、扩容缩容

homepage 是无状态仪表盘:没有数据库、不做持久缓存,单实例资源占用极小。扩容缩容的重点因此不是「加机器」,而是摸清水位、管好日志体量、明确多实例的边界。

5.1 资源水位实测

docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}' homepage-v2.7.0-web homepage-caddy-https

实测输出:

NAME                   CPU %     MEM USAGE / LIMIT     NET I/O           BLOCK I/O
homepage-v2.7.0-web    0.00%     106.7MiB / 23.42GiB   7.53kB / 3.32kB   295kB / 0B
homepage-caddy-https   0.00%     12.85MiB / 23.42GiB   10.3kB / 9.6kB    782kB / 246kB

💡 说明:空闲态 homepage 常驻内存约 107MiB(Next.js 应用基线),Caddy 约 13MiB,CPU 接近 0。加上页面渲染峰值,1GB 内存的小型服务器即可稳定承载;服务集成(widget 探针)增多时内存略有上浮,仍远够不着小机型瓶颈。缩容(降配)前跑一次 docker stats 对照本节水位即可判断余量。

5.2 多实例边界

单机横向扩容 homepage 没有收益(无状态、无并发瓶颈),真正需要多实例的场景是「多机房各部署一套、各自看各自的服务面」。多实例时注意三点:HOMEPAGE_ALLOWED_HOSTS 与 HOMEPAGE_EXTERNAL_URL 按各自访问形态分别配置;Docker socket 只读挂载只反映本机服务,跨机服务改用 docker.yaml 的远程 HTTP/TLS 接入(见《homepage v2.7.0 配置参数详解》第七章);配置目录各自独立,不存在共享状态。

5.3 日志轮转

应用日志写在挂载目录内(/opt/homepage/config/logs/homepage.log),随运行时间增长。用 logrotate 治理,配置 /etc/logrotate.d/homepage:

/opt/homepage/config/logs/*.log {
    weekly
    maxsize 10M
    rotate 8
    missingok
    notifempty
    compress
    delaycompress
    copytruncate
    dateext
}

逐项说明:weekly 每周轮转、maxsize 10M 超过 10MB 提前触发;rotate 8 保留 8 份;missingok/notifempty 日志缺失或为空时跳过不报错;compress+delaycompress 上上一轮的旧日志压缩(最近一份留原文,便于直接 grep);copytruncate 复制后截断原文件——应用持有日志文件句柄,不用 create 模式重建文件,避免句柄指向旧 inode 继续写入;dateext 用日期做轮转后缀。

先做语法与匹配检查(-d 为演练模式,不动文件):

logrotate -d /etc/logrotate.d/homepage

实测输出(输出节选,实测首尾):

reading config file /etc/logrotate.d/homepage
Reading state from file: /var/lib/logrotate/status
Allocating hash table for state file, size 64 entries
Creating new state

再强制轮转一次验证实际效果:

ls -lh /opt/homepage/config/logs/
logrotate -f /etc/logrotate.d/homepage
ls -lh /opt/homepage/config/logs/

实测输出:

total 4.0K
-rw-r--r-- 1 root root 3.8K Oct 25 11:21 homepage.log
total 4.0K
-rw-r--r-- 1 root root    0 Oct 25 11:24 homepage.log
-rw-r--r-- 1 root root 3.8K Oct 25 11:24 homepage.log-20261025

💡 说明:轮转后原文件被截断为 0 字节(应用句柄继续有效),历史内容存为 homepage.log-20261025;因配置了 delaycompress,最近一份不压缩,下一轮才转 .gz。该配置同样作用于排障期产生的日志备份。

六、升级与回滚

homepage 更新节奏快(近期基本每周一个 minor 版本),升级与回滚都必须是「改 tag → 拉镜像 → 重建」三步的低风险操作。版本基线:本文成稿时(2026-10-25)官方最新稳定版为 v2.7.0(2026-10-23 发布,以 GitHub Releases 页实查为准)。

6.1 升级标准流程

以实测的「v2.6.0 → v2.7.0 升级」演示完整流程(未来升级到更新版本时,只需替换目标 tag)。升级前先执行第三章备份。

第 1 步,编辑 /opt/homepage/docker-compose.yml,把 image 的 tag 换成目标版本:

    image: ghcr.io/gethomepage/homepage:v2.7.0

第 2 步,拉取新版本镜像:

cd /opt/homepage
docker compose pull

实测输出:

 Image ghcr.io/gethomepage/homepage:v2.7.0 Pulling 
 Image ghcr.io/gethomepage/homepage:v2.7.0 Pulled 

第 3 步,滚动重建服务(compose 检测到镜像变化自动重建):

docker compose up -d

实测输出:

 Container homepage-v2.7.0-web Recreated 
 Container homepage-v2.7.0-web Started 

第 4 步,三重验证:版本确实切换、探针存活、配置无恙:

docker compose images

实测输出:

CONTAINER             REPOSITORY                     TAG                 PLATFORM            IMAGE ID            SIZE                CREATED
homepage-v2.7.0-web   ghcr.io/gethomepage/homepage   v2.7.0              linux/amd64         19c5c0918445        376MB               36 hours ago
curl -s http://homepage.example.com:8129/api/healthcheck
docker compose logs homepage | grep -m1 "Next.js"

实测输出:

up
homepage-v2.7.0-web  | ▲ Next.js 16.4.1

⚠️ 注意:不要用 latest 标签做升级通道——latest 会跨大版本滚动(v2 → v3),行为变化不可控;升级一律锁定精确版本号(tag 策略对比见《homepage v2.7.0 Docker Compose 部署指南》第二章)。跨大版本升级前先读 Release Notes 的 Breaking Changes,并确认备份在手。

6.2 回滚演练

新版本异常时直接把 tag 换回旧版本重建即可。实测「v2.7.0 → v2.6.0」回滚:

cd /opt/homepage
# 编辑 docker-compose.yml,将 image tag 改回 v2.6.0 后执行:
docker compose up -d

实测输出:

 Container homepage-v2.7.0-web Recreate 
 Container homepage-v2.7.0-web Recreated 
 Container homepage-v2.7.0-web Started 

验证回滚结果与配置保留情况:

docker compose images

实测输出:

CONTAINER             REPOSITORY                     TAG                 PLATFORM            IMAGE ID            SIZE                CREATED
homepage-v2.7.0-web   ghcr.io/gethomepage/homepage   v2.6.0              linux/amd64         3e38fe788223        369MB               7 days ago
curl -s http://homepage.example.com:8129/api/healthcheck
docker compose logs homepage | grep -m1 "Next.js"
curl -s -b cookies.txt http://homepage.example.com:8129/ | python3 -c "
import json,sys,re
m=re.search(r'<script id=\"__NEXT_DATA__\" type=\"application/json\">(.*?)</script>', sys.stdin.read(), re.S)
print('title:', json.loads(m.group(1))['props']['pageProps']['initialSettings'].get('title'))
"

实测输出:

up
homepage-v2.7.0-web  | ▲ Next.js 16.3.8
title: 测试变量仪表盘

💡 说明:v2.6.0 与 v2.7.0 之间配置完全互通(YAML 配置在两个版本间往返,标题、主题、服务分组原样保留)。这就是 3.1 节强调「全部状态=一个目录」的红利:版本回滚不涉及任何数据迁移。若确实回滚失败,还有 3.2 节的备份恢复兜底。

七、故障排查手册

本章沉淀运维期真实发生过的故障,按「现象 → 定位 → 解决」组织。排查通用入口永远是:docker compose ps 看状态 → docker compose logs homepage 看日志 → 按下述案例对号入座。

7.1 探针返回 Host validation failed

现象:/api/healthcheck 返回错误 JSON 而非 up。实测复现(用未被白名单收录的 127.0.0.1:8129 直连):

curl -s http://127.0.0.1:8129/api/healthcheck

实测输出:

{"error":"Host validation failed. See logs for more details."}

定位:日志给出被拒的完整 主机:端口 与修复提示:

docker compose logs --since 10m homepage | grep "Host validation"

实测输出:

homepage-v2.7.0-web  | [2026-10-25T03:27:48.860Z] error: Host validation failed for: 127.0.0.1:8129. Hint: Set the HOMEPAGE_ALLOWED_HOSTS environment variable to allow requests from this host / port.

解决:把日志里 Host validation failed for: 后面的完整串原样加进编排文件的 HOMEPAGE_ALLOWED_HOSTS(逗号分隔、不带空格),docker compose up -d 重建后复测。注意内置白名单只有 localhost:3000 与 127.0.0.1:3000,宿主端口映射后「本机直连」也是新的白名单条目;同一域名走 8129 直连与 8130 反代是两个条目。

7.2 服务反复重启(Restarting),探针连接失败

现象:docker compose ps 状态在 Restarting 循环,探针连接被拒。实测复现(配置目录被误加只读挂载 :ro 且目录为空时):

docker compose up -d

等待数秒后查看状态与日志:

docker compose ps

实测输出:

NAME                  STATUS
homepage-v2.7.0-web   Restarting (1) Less than a second ago
docker compose logs homepage

实测输出(输出节选,实测首尾):

homepage-v2.7.0-web  | ▲ Next.js 16.4.1
homepage-v2.7.0-web  | - Local:         http://[::1]:3000
homepage-v2.7.0-web  | - Network:       http://[::]:3000
homepage-v2.7.0-web  | ✓ Ready in 0ms
homepage-v2.7.0-web  | ✓ Running next.config took 1.3ms
homepage-v2.7.0-web  | ❌ Failed to initialize required config: /app/config/settings.yaml
homepage-v2.7.0-web  | Reason: EROFS: read-only file system, copyfile '/app/src/skeleton/settings.yaml' -> '/app/config/settings.yaml'
homepage-v2.7.0-web  | Hint: Make /app/config writable or manually place the config file.

探针此时连接失败:

curl -s -o /dev/null -w "HTTP %{http_code}\n" http://homepage.example.com:8129/

实测输出:

HTTP 000

定位:Restarting (1) 表示进程以非零码退出被反复拉起;日志 EROFS: read-only file system 指明启动时要把配置骨架复制进 /app/config,而该路径只读。解决:去掉挂载项里的 :ro 后缀(配置目录必须可写——homepage 会在其中写骨架、写日志),重建后恢复 healthy。若业务确实要求只读,必须手工预置全部配置文件并接受无法写日志的代价,不建议。

7.3 降权运行(PUID/PGID)与目录属主

现象:配置 PUID/PGID 以非 root 用户运行时,担心挂载目录属主不匹配导致启动失败。实测结论:homepage 启动脚本会自动把 /app/config 的属主修正为 PUID:PGID。实测把配置目录整体置为 root:root 后设置 PUID=1000/PGID=1000 启动,服务照常 healthy,目录属主被自动改写:

ls -ln /opt/homepage/config | head -4

实测输出:

total 36
-rw-r--r-- 1 1000 1000 289 Oct 25 10:03 bookmarks.yaml
-rw-r--r-- 1 1000 1000  42 Oct 25 10:03 docker.yaml
-rw-r--r-- 1 1000 1000  31 Oct 25 10:01 kubernetes.yaml

结论:属主不匹配无需人工干预,启动即自愈;但仍建议把挂载源目录属主预先 chown 成与 PUID/PGID 一致的值,避免依赖隐式改写。若还叠加了只读挂载则同 7.2 会直接崩溃。

7.4 端口占用核查

现象:docker compose up -d 报端口分配失败(port is already allocated)。定位用 ss 看宿主端口当前占用者:

ss -ltnp | grep 8129

实测输出(正常状态,homepage 栈自己占用):

LISTEN 0      4096         0.0.0.0:8129       0.0.0.0:*    users:(("docker-proxy",pid=1576849,fd=8))              
LISTEN 0      4096            [::]:8129          [::]*    users:(("docker-proxy",pid=1576855,fd=8))              

解决:占用者是 docker-proxy 说明已被本栈或另一套编排占用,先 docker compose ls 或逐栈 docker compose ps 找到占端口的编排停掉/改端口;改宿主端口后务必同步 HOMEPAGE_ALLOWED_HOSTS(见 7.1)与 HOMEPAGE_EXTERNAL_URL。

7.5 配置改坏后的排查路径

现象:改完 YAML 后服务区块消失、widget 无数据,但页面仍能打开。定位顺序:先跑配置自检端点拿精确错误(文件名、原因、行号),再看日志确认加载失败记录:

curl -s -b cookies.txt http://homepage.example.com:8129/api/validate

健康时返回 [];有错时返回形如 [{"name":"YAMLException","config":"services.yaml","reason":"deficient indentation","mark":{"line":25}}] 的错误数组,按 line 行号修缩进即可。homepage 对坏配置的容错是「跳过该文件、页面不崩」,判断配置健康必须用该端点或日志,不能只看页面。完整的坏 YAML 制造、复现与修复实录见《homepage v2.7.0 配置参数详解》第八章案例 4。

八、常见问题

Q1:备份时需要停服务吗?

不需要。备份对象是普通文件(几 KB 的 YAML 与日志),tar 直接读即可;要求严格时间点一致时再 docker compose stop → 打包 → start。恢复时则必须先 down——恢复是把文件覆盖回挂载目录,服务运行中覆盖会造成读写竞争。

Q2:升级或回滚会丢配置吗?

不会。配置全部在挂载目录中,镜像替换不触碰该目录。本文 6.1/6.2 两节在 v2.6.0 ↔ v2.7.0 之间双向演练,页面标题(占位符渲染值)与全部配置项原样保留。跨大版本升级(如未来 v3)先读 Release Notes 并备份,仍以「改精确 tag → pull → up -d」推进。

Q3:日常巡检应该盯哪几个指标?

四个:docker compose ps 为 healthy;/api/healthcheck 返回 up;/api/validate 返回 [];du -sh /opt/homepage/config 维持 KB 级基线。前两个回答「活着」,第三个回答「配置没改坏」,第四个回答「没有被塞进异常文件」。

Q4:HTTPS 访问浏览器提示证书不受信任?

这是 Caddy 自签方案的预期行为:本地验证域名没有公网解析,证书由 Caddy 内部 CA 签发,浏览器不信任属正常现象。生产换真域名后删掉 Caddyfile 里的 tls internal 一行即可自动签发受信证书(自签与 ACME 两种方式的取舍详见《homepage v2.7.0 Docker Compose 部署指南》第十章)。

九、参考链接