---
name: pve
description: Use when user asks about VMs, virtual machines, PVE, Proxmox, port forwarding, or wants to create/start/stop/list VMs. Triggers on "VM", "虛擬機", "PVE", "port forward", "開機", "關機", "建 VM".
---

# PVE 管理

透過 SSH 操作 Proxmox VE。連線資訊在 CLAUDE.md，這裡只記概念與指令。

## 連線（直接 alias，不要硬寫 IP/port）

PVE host 跟所有 VM 都該在 local `~/.ssh/config` 有 alias，名字 = VM name：

```bash
ssh pve              # PVE host (root)
ssh afc              # VM "afc" (VMID 101)
ssh raphael          # VM "raphael" (VMID 102)
```

絕對不要用 `ssh -p 50101 user@140.113.194.229` 這種裸命令 — 那是 alias 漏建的訊號，先補 alias 再做事。

## 查詢

```bash
ssh pve "qm list"                                          # 所有 VM
ssh pve "qm config <VMID>"                                 # VM 設定
ssh pve "iptables -t nat -L PREROUTING -n --line-numbers"  # port forwarding
```

## 開機 / 關機 / 重啟

```bash
ssh pve "qm start|stop|reboot <VMID>"
```

## 建立 VM（嚴格按此順序）

Template（VMID 9000）已包含：2 cores, host CPU, 8GB RAM, 100GB disk, virtio-scsi-single, serial console, cloud-init (NYCU mirror, ciuser=user, nameserver=8.8.8.8, SSH keys)。Clone 後只需設 IP，RAM 不是 8GB 才改。

### Step 1: 確認 VMID 可用

```bash
ssh pve "qm list"
```

確認目標 VMID 不存在。

### Step 2: Clone + 設定 + Port Forwarding（全部在開機前完成）

```bash
# Clone
ssh pve "qm clone 9000 <VMID> --name <NAME> --full true --storage local-lvm"

# 設定靜態 IP（必要）
ssh pve "qm set <VMID> --ipconfig0 ip=10.10.10.<VMID>/24,gw=10.10.10.1"

# 設定 RAM（僅在非 8GB 時）
ssh pve "qm set <VMID> --memory <RAM_MB>"

# SSH port forwarding（慣例：50<VMID> → :22）
ssh pve "iptables -t nat -A PREROUTING -p tcp --dport 50<VMID> -j DNAT --to-destination 10.10.10.<VMID>:22"

# 持久化 iptables
ssh pve "iptables-save > /etc/iptables/rules.v4"
```

### Step 3: 開機

```bash
ssh pve "qm start <VMID>"
```

### Step 4: 等待並驗證（開機後約 30 秒）

開機後直接 SSH 驗證，不要依賴 guest agent（可能還沒啟動）。先用裸命令驗一次（這時 alias 還沒建）：

```bash
sleep 30 && ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 -p 50<VMID> user@140.113.194.229 "hostname && ip -4 addr show eth0 | grep inet"
```

### Step 5: 寫入 local SSH alias（**不可省略**）

驗證通過後，立即在 `~/.ssh/config` 加 alias，讓未來 `ssh <name>` 直接可用：

1. 確認檔案有 alias 共用 block（一次性 setup，新 VM 加進 host list）：

   ```ssh-config
   # === PVE host (140.113.194.229) ===
   Host pve
     HostName 140.113.194.229
     User root
     Port 22
     ForwardAgent yes

   # === PVE VMs ===
   # Per-host Port stanzas (each VM)
   Host afc
     Port 50101
   Host <NAME>
     Port 50<VMID>

   # Shared defaults (放在 per-host stanzas 之後；SSH 是 first-match-wins，
   # Port 已被前面的 per-host 鎖住，這個 block 補其他預設值)
   Host afc <NAME> ...
     HostName 140.113.194.229
     User user
     IdentityFile ~/.ssh/id_ed25519
     ForwardAgent yes
     ServerAliveInterval 60
   ```

2. 加完後驗證 alias 可用：

   ```bash
   ssh <NAME> "hostname"   # 應該回 <NAME>
   ```

`ForwardAgent yes` 是預設 — 為了讓 VM 上的 git 可以 borrow local 的 GitHub key（避免每台 VM 都要單獨綁 GitHub）。如果是給別人用的 VM，alias 可以拿掉這行。

### 完成後輸出

告知使用者：
- `ssh <NAME>` 直接可用
- 內網 IP：`10.10.10.<VMID>`
- 外部 SSH port：`50<VMID>`
- iptables 已持久化

## Port Forwarding 管理

```bash
# 新增
ssh pve "iptables -t nat -A PREROUTING -p tcp --dport <EXT_PORT> -j DNAT --to-destination 10.10.10.<VMID>:<INT_PORT>"

# 刪除（先 list 確認 line number）
ssh pve "iptables -t nat -D PREROUTING <LINE_NUMBER>"

# 修改後一定要持久化
ssh pve "iptables-save > /etc/iptables/rules.v4"
```

## 刪除 VM

```bash
ssh pve "qm stop <VMID> && qm destroy <VMID> --purge"
```

刪除後三件事一起做：
1. 清對應的 iptables 規則並 `iptables-save`
2. 從 local `~/.ssh/config` 移除該 host alias
3. （若有）`ssh-keygen -R '[140.113.194.229]:50<VMID>'` 清 known_hosts

## 重命名 VM

```bash
ssh pve "qm set <VMID> --name <NEW_NAME>"
```

別忘了同步 local `~/.ssh/config` 把舊 alias 改成新名字。

## 慣例

- **VMID**: `1XX` = service VMs, `2XX` = infra（gateway 等），`9XXX` = templates
- **IP**: `10.10.10.{VMID}`（VMID 100 → .100）
- **Port forwarding**: SSH 用 `host:50{VMID} → :22`
- **RAM 預設**: 8GB（使用者指定其他值才改）
- **Template**: VMID 9000（ubuntu-24.04-template）
- **SSH alias name**: 跟 VM name 對齊，`ssh <name>` 一律可用
- **Local SSH config**: `~/.ssh/config` 是 single source of truth，不准用裸 ssh 命令繞過

## Rules

- 建立 VM 時**一定先設定完所有東西再開機**，避免多次 reboot
- 驗證用 SSH 連線，不依賴 QEMU guest agent
- **建立 VM 後 Step 5 必跑** — 沒寫 alias = 任務沒完
- 刪除 / 重命名 VM 時，iptables、ssh config、known_hosts 三處同步
- 刪除 VM、修改 port forwarding 前**一定要跟使用者確認**
- Port forwarding 異動後一定要 `iptables-save` 持久化
- 看到 `ssh -p 50xxx user@140.113...` 這種裸命令，第一反應是「alias 漏了，先補」
