docs(soft-serve): 镜像固定 v0.12.2 + 备份 sidecar + 非 root 运行 (W1N-244..248)
- 镜像 pinned charmcli/soft-serve:v0.12.2(GHCR 为 dev/nightly 源,无 v0.12.x tag) - soft-serve-backup sidecar:每日 02:00 sqlite .backup + repos-config 打包,03:00 prune 保留 14 份 - 非 root 运行(user 1000:1000),data chown;ssh.public_url 修复 - compose 源码参考:compose/soft-serve/(服务器文件为准)
This commit is contained in:
@@ -59,7 +59,7 @@ not maintain a second copy of the machine table here.
|
|||||||
| SMTP `mx2.windy.me:587` (STARTTLS) or `:465` | mx2 | client submission; full email + mailbox password — [runbook](runbooks/mailcow-smtp-client.md) |
|
| SMTP `mx2.windy.me:587` (STARTTLS) or `:465` | mx2 | client submission; full email + mailbox password — [runbook](runbooks/mailcow-smtp-client.md) |
|
||||||
| IMAP `mx2.windy.me:993` | mx2 | same mailbox credentials |
|
| IMAP `mx2.windy.me:993` | mx2 | same mailbox credentials |
|
||||||
| https://auth.wsvc.info | us2 (`/opt/vaultwarden`) | Vaultwarden (Postgres, **operational**) — client Server URL |
|
| https://auth.wsvc.info | us2 (`/opt/vaultwarden`) | Vaultwarden (Postgres, **operational**) — client Server URL |
|
||||||
| `repo.windy.me:2222` | us2 (`/opt/soft-serve`) | Soft Serve (stub details) |
|
| `repo.windy.me:2222` | us2 (`/opt/soft-serve`) | Soft Serve git — v0.12.2 pinned, backup sidecar; details in [hosts/us2.wsvc.info.md](hosts/us2.wsvc.info.md) |
|
||||||
| DNS `ns1.wsvc.info:53` | hk2 (`/opt/pdns`, Auth **5.0.6**) | PowerDNS auth — zones `windy.me`, `wsvc.info`, `chans.xyz` |
|
| DNS `ns1.wsvc.info:53` | hk2 (`/opt/pdns`, Auth **5.0.6**) | PowerDNS auth — zones `windy.me`, `wsvc.info`, `chans.xyz` |
|
||||||
| https://pdns.wsvc.info | hk2 (`poweradmin`) | Poweradmin UI |
|
| https://pdns.wsvc.info | hk2 (`poweradmin`) | Poweradmin UI |
|
||||||
| https://pgweb.wsvc.info | hk2 (`pgweb`) | PowerDNS Postgres browser |
|
| https://pgweb.wsvc.info | hk2 (`pgweb`) | PowerDNS Postgres browser |
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ files: every secret is a `${VAR}` reference resolved from the **server-local
|
|||||||
| `vaultwarden` | us2 (`/opt/vaultwarden`) | `compose/vaultwarden/compose.yml` | static file + `compose-deploy.yml` |
|
| `vaultwarden` | us2 (`/opt/vaultwarden`) | `compose/vaultwarden/compose.yml` | static file + `compose-deploy.yml` |
|
||||||
| `pdns` | hk2 (`/opt/pdns`) | `compose/pdns/compose.yml` | static file + `compose-deploy.yml` |
|
| `pdns` | hk2 (`/opt/pdns`) | `compose/pdns/compose.yml` | static file + `compose-deploy.yml` |
|
||||||
| `pgdb` | pgdb (`/opt/database`, 无 ansible) | `compose/pgdb/compose.yml` | static file(手动部署:scp → `docker compose config -q` → `up -d`;服务器文件名 `docker-compose.yml`) |
|
| `pgdb` | pgdb (`/opt/database`, 无 ansible) | `compose/pgdb/compose.yml` | static file(手动部署:scp → `docker compose config -q` → `up -d`;服务器文件名 `docker-compose.yml`) |
|
||||||
|
| `soft-serve` | us2 (`/opt/soft-serve`) | `compose/soft-serve/compose.yml` (+ `Dockerfile.backup`, `scripts/`) | static file(参考镜像, 未接入 compose-deploy; 服务器文件为准) |
|
||||||
| `adguardhome` | dns.windy.lan (`/opt/adguardhome`) | — (待从 LAN 提取) | static file (pending) |
|
| `adguardhome` | dns.windy.lan (`/opt/adguardhome`) | — (待从 LAN 提取) | static file (pending) |
|
||||||
| `unifi` | ubnt (`/home/windy/unifi-9`) | — (待从 LAN 提取) | static file (pending) |
|
| `unifi` | ubnt (`/home/windy/unifi-9`) | — (待从 LAN 提取) | static file (pending) |
|
||||||
| `wireguard` | us4 (`/opt/wireguard`) | `ansible/templates/wireguard-compose.yml.j2` | role-rendered (inventory vars) |
|
| `wireguard` | us4 (`/opt/wireguard`) | `ansible/templates/wireguard-compose.yml.j2` | role-rendered (inventory vars) |
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Soft Serve initial admin public key (used only on first boot)
|
||||||
|
SOFT_SERVE_INITIAL_ADMIN_KEYS=ssh-ed25519 AAAA... # replace with admin public key
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
FROM alpine:3.20
|
||||||
|
RUN apk add --no-cache sqlite tzdata
|
||||||
|
WORKDIR /scripts
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
services:
|
||||||
|
soft-serve:
|
||||||
|
image: charmcli/soft-serve:v0.12.2
|
||||||
|
container_name: soft-serve
|
||||||
|
restart: unless-stopped
|
||||||
|
# non-root (uid 1000 = windy; 与 backup sidecar BACKUP_UID 一致)
|
||||||
|
user: "1000:1000"
|
||||||
|
|
||||||
|
environment:
|
||||||
|
SOFT_SERVE_DATA_PATH: /var/lib/soft-serve
|
||||||
|
SOFT_SERVE_INITIAL_ADMIN: windy
|
||||||
|
SOFT_SERVE_INITIAL_ADMIN_KEYS: ${SOFT_SERVE_INITIAL_ADMIN_KEYS}
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- ./data:/var/lib/soft-serve
|
||||||
|
- soft-serve-app:/soft-serve
|
||||||
|
|
||||||
|
networks:
|
||||||
|
- traefik
|
||||||
|
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
|
||||||
|
# SSH over TCP via Traefik (entryPoint ssh -> container port 23231)
|
||||||
|
- traefik.tcp.routers.softserve-ssh.entrypoints=ssh
|
||||||
|
- traefik.tcp.routers.softserve-ssh.rule=HostSNI(`*`)
|
||||||
|
- traefik.tcp.routers.softserve-ssh.tls=false
|
||||||
|
- traefik.tcp.services.softserve-ssh.loadbalancer.server.port=23231
|
||||||
|
|
||||||
|
soft-serve-backup:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.backup
|
||||||
|
container_name: soft-serve-backup
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./data:/data:ro
|
||||||
|
- ./backups:/backup
|
||||||
|
- ./scripts:/scripts
|
||||||
|
environment:
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
BACKUP_UID: 1000
|
||||||
|
BACKUP_GID: 1000
|
||||||
|
entrypoint: >
|
||||||
|
/bin/sh -ec "
|
||||||
|
umask 077 &&
|
||||||
|
touch /backup/backup.log &&
|
||||||
|
crontab /scripts/crontab.txt &&
|
||||||
|
echo '[INFO] soft-serve backup cron installed' &&
|
||||||
|
crond -f -l 8
|
||||||
|
"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
soft-serve-app:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik:
|
||||||
|
external: true
|
||||||
|
name: vw-net
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
umask 077
|
||||||
|
D() { date "+%Y-%m-%d %H:%M:%S"; }
|
||||||
|
TS=$(date +%Y%m%d_%H%M%S)
|
||||||
|
OUT="/backup/soft-serve_${TS}"
|
||||||
|
mkdir -p "$OUT"
|
||||||
|
echo "[$(D)] Starting soft-serve backup -> $OUT"
|
||||||
|
tar czf "$OUT/repos-config.tar.gz" -C /data repos hooks config.yaml ssh
|
||||||
|
sqlite3 /data/soft-serve.db ".backup '$OUT/soft-serve.db'"
|
||||||
|
chmod 600 "$OUT/repos-config.tar.gz" "$OUT/soft-serve.db"
|
||||||
|
if [ -n "${BACKUP_UID:-}" ] && [ -n "${BACKUP_GID:-}" ]; then
|
||||||
|
chown -R "$BACKUP_UID:$BACKUP_GID" "$OUT" /backup/backup.log
|
||||||
|
fi
|
||||||
|
echo "[$(D)] Backup OK: $(du -sh "$OUT" | cut -f1)"
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Run soft-serve backup daily at 02:00
|
||||||
|
0 2 * * * /bin/sh /scripts/backup.sh >> /backup/backup.log 2>&1
|
||||||
|
# Prune backups older than 14 days daily at 03:00
|
||||||
|
0 3 * * * /bin/sh /scripts/prune.sh >> /backup/backup.log 2>&1
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
D() { date "+%Y-%m-%d %H:%M:%S"; }
|
||||||
|
ls -dt /backup/soft-serve_* 2>/dev/null | tail -n +15 | xargs -r rm -rf
|
||||||
|
echo "[$(D)] Pruned. Kept $(ls -d /backup/soft-serve_* 2>/dev/null | wc -l) backups (max 14)"
|
||||||
+10
-1
@@ -80,7 +80,16 @@ ansible-playbook playbooks/compose-reconcile.yml --limit vaultwarden \
|
|||||||
|
|
||||||
| Container | Status | Image / notes |
|
| Container | Status | Image / notes |
|
||||||
|-----------|--------|---------------|
|
|-----------|--------|---------------|
|
||||||
| `soft-serve` | Up | `ghcr.io/charmbracelet/soft-serve:latest` (`repo.windy.me:2222`) |
|
| `soft-serve` | Up | `charmcli/soft-serve:v0.12.2` (`repo.windy.me:2222`) |
|
||||||
|
| `soft-serve-backup` | Up | alpine + sqlite3 sidecar (daily backup 02:00 / prune 03:00, crond) |
|
||||||
|
|
||||||
|
Soft Serve details (verified/updated 2026-08-30; 核查 [W1N-244](https://linear.app/w1ndy/issue/W1N-244), 修复 [W1N-245](https://linear.app/w1ndy/issue/W1N-245)/[W1N-246](https://linear.app/w1ndy/issue/W1N-246)/[W1N-247](https://linear.app/w1ndy/issue/W1N-247)):
|
||||||
|
- `/opt/soft-serve/compose.yml` (+ `Dockerfile.backup`, `scripts/`, `backups/`); data `/opt/soft-serve/data` → `/var/lib/soft-serve` (sqlite `soft-serve.db`, 12 repos + `windyboy`); env `.env` (`SOFT_SERVE_INITIAL_ADMIN_KEYS` = admin pubkey, first-boot only). 仓库镜像: `compose/soft-serve/`(参考, 服务器文件为准)
|
||||||
|
- No host ports published: Traefik TCP entrypoint `ssh` (`:2222` → `soft-serve:23231`, `HostSNI(*)`, `tls=false`) on `vw-net`. Container listens 23231 SSH / 23232 HTTP (git smart-http, no web UI) / 9418 git / 23233 stats (localhost only)
|
||||||
|
- **镜像已固定** `charmcli/soft-serve:v0.12.2`(digest `sha256:554cc770…`, Docker Hub 稳定源; GHCR 为 dev/nightly 源且无 v0.12.x tag); `/soft-serve` 由 named volume `soft-serve_soft-serve-app` 承载, 旧匿名卷已清理(2026-08-30)
|
||||||
|
- **非 root 运行**(2026-08-30, [W1N-248](https://linear.app/w1ndy/issue/W1N-248)): compose `user: "1000:1000"`(uid=windy, 镜像无内置用户); data 全量 `chown 1000:1000`, 容器内 `id` = uid 1000, 功能验证通过
|
||||||
|
- **`ssh.public_url` 已修复**(2026-08-30): `config.yaml` → `ssh://git@repo.windy.me:2222`; 失效 env `SOFT_SERVE_SSH_PUBLIC_URL` 已删(v0.12 不读取); `http`/`git` public_url 保持 `localhost`(未对外暴露)
|
||||||
|
- **备份**: sidecar `soft-serve-backup` 每日 02:00 → `backups/soft-serve_<TS>/{repos-config.tar.gz, soft-serve.db}`(db 用 `sqlite3 .backup` 在线快照), 03:00 prune 保留 14 份; 产物 chown windy:windy 600(含 `ssh/` host keys)。**恢复**: `docker compose stop soft-serve` → 解包 `repos-config.tar.gz` + 放回 `soft-serve.db` 到 `data/` → `docker compose up -d`。异地副本(hk2/WSL 每日拉取)= follow-up(见 W1N-247)
|
||||||
| `traefik` | Up | `traefik:v3.6.2` (`/opt/traefik`, public `:80`/`:443`) |
|
| `traefik` | Up | `traefik:v3.6.2` (`/opt/traefik`, public `:80`/`:443`) |
|
||||||
| `nghttpx-proxy` + `squid-backend` | Up | HTTP forward-proxy stack (`/opt/nghttpx`), network `nghttpx_internal-net`; details TBD |
|
| `nghttpx-proxy` + `squid-backend` | Up | HTTP forward-proxy stack (`/opt/nghttpx`), network `nghttpx_internal-net`; details TBD |
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user