7e7130172e
Two related changes shipped together. The stack rename is independent but adding `vllm-reward` to the existing `vllm-qwen3` would have made that name actively misleading. **Rename:** `stacks/vllm-qwen3/ → stacks/vllm/`. Updated all in-repo references (README.md root, servers/ana-ml2/, stacks/llama-swap/, configs/restic/ana-ml2/, docs/runbooks/disaster-recovery.md). Two intentional history mentions retained (servers/ana-ml2 + stacks/vllm README). **Add `vllm-reward` service:** serves Skywork-Reward-V2-Llama-3.1-8B-AWQ on port 8003. The AWQ output is a locally-quantized model (not from HF), so bind-mounts `/tank/aimodels/llm:/local-models:ro` rather than the shared HF cache. Model config.json declares LlamaForSequenceClassification which vLLM's pooling runner picks up automatically — produces a single reward score per input via /classify. **Flag note:** the user's spec listed `--task classify`, but vLLM 0.19.1 deprecated --task in favor of --runner pooling (model architecture in config.json drives the classification head). Compose uses --runner pooling with a comment explaining the substitution. **GPU memory:** no rebalance needed — production had already tuned EMBED/RERANK down from 0.40 to 0.20 each (canonical .env.example now matches reality). Adding REWARD at 0.30 totals 0.70, leaving ~14 GB headroom on the 48 GB Ada. **Server-side:** brought existing vllm-qwen3 down, mv'd /opt/docker/compose/vllm-qwen3 → /opt/docker/compose/vllm, appended REWARD_* lines to existing .env (preserving API_KEY/HF_TOKEN), deployed new compose via scripts/deploy-stack.sh, brought all 3 services up. **Smoke tests:** - /health on 8001/8002/8003 → 200 - /v1/models on 8003 → lists Skywork/Skywork-Reward-V2-Llama-3.1-8B-AWQ with max_model_len 16384 - /classify with a sample conversation → returns LABEL_0 with prob 0.9999 (single-output regression-style reward score, expected shape for a reward model)
252 lines
8.4 KiB
Markdown
252 lines
8.4 KiB
Markdown
# restic / ana-ml2
|
|
|
|
**Bare metal** — ana-ml2 runs directly on a server chassis, not on any
|
|
Proxmox hypervisor, so vzdump doesn't cover it. This is the sole backup
|
|
for the host's configuration and is the highest-stakes backup target in
|
|
the fleet.
|
|
|
|
## What's backed up
|
|
|
|
| Path | Purpose |
|
|
|---|---|
|
|
| `/opt/docker` | Compose files + config bind mounts (~110 MB) |
|
|
| `/etc` | Host config — systemd, ssh, chrony, apparmor, apt, etc. |
|
|
| `/root` | Root's ad-hoc scripts, shell history, ssh keys |
|
|
| `/var/lib/docker/volumes` | Named volumes used by running stacks (small — models live elsewhere) |
|
|
|
|
## What's **not** backed up (by design)
|
|
|
|
- **`/tank/*`** — model caches (HF hub, GGUFs, ComfyUI checkpoints,
|
|
Kokoro voices, Vibevoice weights, Parakeet weights). Regenerable from
|
|
upstream; storing them would blow the repo size budget.
|
|
- `/root/.cache`, `/root/.npm`, shell-noise caches
|
|
- Any `*.log`, `*.pid`, `.../logs/` directories
|
|
|
|
## Stacks on this host
|
|
|
|
All inference/AI-adjacent — none use relational databases, so no
|
|
pre-backup DB hook is required. Contrast with `configs/restic/ana-docker/`
|
|
where synapse/seafile/vaultwarden DB dumps run first.
|
|
|
|
- `llama-swap` — GGUF swapper (llama.cpp)
|
|
- `vllm` — embedding + rerank + reward classifier
|
|
- `comfyui`, `kokoro`, `parakeet`, `vibevoice`
|
|
- `beszel-agent-ana`, `dozzle-agent-ana`, `dockge`
|
|
|
|
## Deploy (one-time setup)
|
|
|
|
### 1. Credentials (repo already exists — reuse)
|
|
|
|
The Anaheim rest-server already has an `ana-ml2` entry in its `.htpasswd`
|
|
and a repo at `/ana-ml2/` from a prior pass. Reusing both keeps the
|
|
snapshot history consolidated.
|
|
|
|
On ana-ml2, install the two secret files from the existing values (both
|
|
live in your password manager):
|
|
|
|
```bash
|
|
ssh -t ana-ml2 '
|
|
sudo install -d -o root -g root -m 0700 /etc/restic /var/lib/restic &&
|
|
sudo tee /etc/restic/restic.env > /dev/null <<EOF
|
|
RESTIC_REPOSITORY=rest:http://ana-ml2:<EXISTING-HTPASSWD-PASSWORD>@10.250.50.70:8000/ana-ml2/
|
|
EOF
|
|
sudo chmod 600 /etc/restic/restic.env &&
|
|
|
|
sudo tee /etc/restic/password > /dev/null <<EOF
|
|
<EXISTING-REPO-PASSPHRASE>
|
|
EOF
|
|
sudo chmod 600 /etc/restic/password
|
|
'
|
|
```
|
|
|
|
### 2. Verify creds against the existing repo
|
|
|
|
Before deploying the profile, prove the secrets are correct. The
|
|
`/etc/restic/restic.env` and `/etc/restic/password` files are root:600,
|
|
so the env-file read AND the restic invocation both need to run inside
|
|
the same sudo shell — otherwise `$(cat …)` runs as the login user,
|
|
fails silently, and restic complains about a missing repository:
|
|
|
|
```bash
|
|
ssh -t ana-ml2 '
|
|
sudo bash -c "
|
|
set -a
|
|
. /etc/restic/restic.env
|
|
set +a
|
|
RESTIC_PASSWORD_FILE=/etc/restic/password restic snapshots
|
|
"
|
|
'
|
|
```
|
|
|
|
Expected: either a list of prior snapshots (from the earlier
|
|
docker-files pass) or `no snapshots found` — both mean auth is good.
|
|
If you see `Fatal: wrong password or no key found`, the passphrase in
|
|
`/etc/restic/password` doesn't match the repo — check your password
|
|
manager or rotate (see "Recreating the repo" below).
|
|
|
|
**Do not run `restic init`** — the repo is already initialized; init
|
|
against an existing repo errors out deliberately.
|
|
|
|
### Recreating the repo (lost passphrase / starting fresh)
|
|
|
|
If `restic snapshots` returns `wrong password or no key found` and the
|
|
passphrase can't be recovered, the old repo's data is unrecoverable and
|
|
the cleanest path is to wipe and reinit. If you're truly starting from
|
|
zero (no prior htpasswd entry or repo at all), skip the
|
|
`htpasswd` step — only the init is needed.
|
|
|
|
The rest-server's data root is `/mnt/backup/restic/repo/ana/` on
|
|
ana-docker (NFS-mounted from the Debian NAS at 10.250.50.50). Per-host
|
|
repos are direct subdirs — for `ana-ml2` that's
|
|
`/mnt/backup/restic/repo/ana/ana-ml2/`. See
|
|
`stacks/rest-server-ana/README.md` for the full sibling layout
|
|
(`ana/`, `esh/`, `nh3/`).
|
|
|
|
```bash
|
|
# 1. Wipe the old repo content on ana-docker. `--append-only` blocks
|
|
# restic's own delete ops but not direct filesystem removal — this
|
|
# is intentional for exactly this case.
|
|
ssh -t ana-docker '
|
|
sudo rm -rf /mnt/backup/restic/repo/ana/ana-ml2 &&
|
|
sudo docker restart rest-server
|
|
'
|
|
|
|
# 2. Optional: rotate the htpasswd password for ana-ml2 at the same time.
|
|
# Produces a new hash; replace in place. (Skip if the existing
|
|
# htpasswd password is still trusted.)
|
|
ssh ana-docker 'docker run --rm httpd:2.4-alpine htpasswd -nbB ana-ml2 "<new-pw>"' \
|
|
| ssh -t ana-docker 'sudo sed -i "/^ana-ml2:/d" /mnt/backup/restic/repo/ana/.htpasswd && sudo tee -a /mnt/backup/restic/repo/ana/.htpasswd >/dev/null'
|
|
|
|
# 3a. Init the repo — interactively prompt for a passphrase you generated
|
|
# in your password manager ahead of time. restic asks twice (init +
|
|
# confirm). This keeps the passphrase out of shell history / transcripts.
|
|
ssh -t ana-ml2 '
|
|
sudo bash -c "
|
|
set -a
|
|
. /etc/restic/restic.env
|
|
set +a
|
|
restic init
|
|
"
|
|
'
|
|
|
|
# 3b. Install the same passphrase into /etc/restic/password so timers can
|
|
# run unattended. `cat > file` + Ctrl-D avoids the passphrase ever
|
|
# landing in your shell history or the ssh command line.
|
|
ssh -t ana-ml2 'sudo bash -c "cat > /etc/restic/password && chmod 600 /etc/restic/password"'
|
|
# Terminal waits for input:
|
|
# - paste the same passphrase
|
|
# - press Enter
|
|
# - press Ctrl-D on an empty line
|
|
# (restic strips the trailing newline when reading the file.)
|
|
|
|
# 3c. Verify the file-based passphrase agrees with what init registered.
|
|
ssh -t ana-ml2 '
|
|
sudo bash -c "
|
|
set -a
|
|
. /etc/restic/restic.env
|
|
set +a
|
|
RESTIC_PASSWORD_FILE=/etc/restic/password restic snapshots
|
|
"
|
|
'
|
|
# Expect: "no snapshots found" — repo is initialized and both paths
|
|
# (interactive and file-based) decrypt it.
|
|
```
|
|
|
|
**Alternative (random machine-generated passphrase):** if you want restic
|
|
to generate the passphrase rather than supplying one from your password
|
|
manager, replace 3a/3b with:
|
|
|
|
```bash
|
|
ssh -t ana-ml2 '
|
|
sudo bash -c "
|
|
openssl rand -base64 48 | tr -d \"\\n\" > /etc/restic/password
|
|
chmod 600 /etc/restic/password
|
|
cat /etc/restic/password # copy this to your password manager NOW
|
|
echo
|
|
set -a; . /etc/restic/restic.env; set +a
|
|
RESTIC_PASSWORD_FILE=/etc/restic/password restic init
|
|
"
|
|
'
|
|
```
|
|
|
|
The passphrase prints to the terminal exactly once — don't close the
|
|
window before copying it. Losing it = losing this repo.
|
|
|
|
### 3. Install resticprofile on ana-ml2
|
|
|
|
The host has `restic` but not `resticprofile`. From the resticprofile
|
|
docs:
|
|
|
|
```bash
|
|
ssh -t ana-ml2 '
|
|
curl -sfL https://raw.githubusercontent.com/creativeprojects/resticprofile/master/install.sh | sudo sh -s -- -b /usr/local/bin
|
|
/usr/local/bin/resticprofile --version
|
|
'
|
|
```
|
|
|
|
### 4. Deploy the profile
|
|
|
|
```bash
|
|
scp configs/restic/ana-ml2/profiles.yaml ana-ml2:/tmp/profiles.yaml
|
|
ssh -t ana-ml2 '
|
|
sudo install -o root -g root -m 0644 /tmp/profiles.yaml /etc/restic/profiles.yaml &&
|
|
rm /tmp/profiles.yaml
|
|
'
|
|
```
|
|
|
|
### 5. Generate + enable the systemd timers
|
|
|
|
```bash
|
|
ssh -t ana-ml2 '
|
|
sudo resticprofile --config /etc/restic/profiles.yaml schedule --all &&
|
|
systemctl list-timers "resticprofile*"
|
|
'
|
|
```
|
|
|
|
`resticprofile schedule` writes the unit files into `/etc/systemd/system/`
|
|
and enables them. You should see three timers: backup, forget, check.
|
|
|
|
### 6. Fire a manual backup to verify
|
|
|
|
```bash
|
|
ssh -t ana-ml2 'sudo resticprofile --config /etc/restic/profiles.yaml backup --verbose'
|
|
```
|
|
|
|
Expected output: a fresh snapshot id and a line summarizing added data
|
|
size + file count.
|
|
|
|
Cross-check from the hub:
|
|
|
|
```bash
|
|
ssh ana-docker 'docker exec backrest ls /repos/ana-ml2/'
|
|
```
|
|
|
|
Or in Backrest UI (http://10.250.50.70:9898), `ana-ml2` shows up as a new
|
|
repo with one snapshot.
|
|
|
|
## Ongoing
|
|
|
|
Timers run at 01:00 / 03:00 / Sunday 05:00 (matching the rest of the
|
|
fleet). Monitor via the `schedule-log` entries or Backrest's web UI.
|
|
|
|
## Restoring
|
|
|
|
```bash
|
|
# List snapshots
|
|
ssh -t ana-ml2 'sudo resticprofile --config /etc/restic/profiles.yaml snapshots'
|
|
|
|
# Restore a single path
|
|
ssh -t ana-ml2 'sudo resticprofile --config /etc/restic/profiles.yaml restore --target /tmp/restore latest --path /opt/docker'
|
|
```
|
|
|
|
## Gotchas specific to this host
|
|
|
|
- **Large `/tank` is deliberately excluded** — don't edit source paths
|
|
to include it without a plan for the ~TB of model data.
|
|
- **Repo passphrase is irreplaceable.** Losing `/etc/restic/password`
|
|
without a copy elsewhere = losing every snapshot in the `ana-ml2`
|
|
repo. Store a copy in your password manager on day one.
|
|
- **`/var/lib/docker/volumes` assumes docker uses the default data
|
|
root.** If you ever switch to a custom dockerd data-root, update the
|
|
source list.
|