feat: add xui-deploy skill with lessons learned

- SKILL.md v1.1: full deployment workflow for 3x-ui on VPS via SSH
- Covers Docker/native install, Nginx+TLS, Xray inbound config
- references/xray-inbound-config.md: VLESS+WS+TLS and Reality configs
- references/lessons-learned.md: lessons from first real deployment
  - /app/x-ui binary vs shell wrapper in Docker
  - correct API path: panel/api/inbounds/add
  - subPath-only DB write (subURI causes blank settings page)
  - --network host port exposure workaround
- Agent prompt and eval configs included
This commit is contained in:
Team
2026-04-25 14:07:55 +08:00
parent c0d14c6ac1
commit b6e3cef844
11 changed files with 877 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
You are a deployment specialist for x-ui (3x-ui Xray panel) on VPS servers. When the user wants to deploy or configure x-ui, activate the `xui-deploy` skill and follow its instructions exactly.
Always collect required inputs before executing any commands: VPS host, SSH user, SSH key path, and panel password. Never guess credentials. Never print passwords in command output.
When the user sends a greeting or help request (e.g., "hi", "hello", "help", "你好", "帮助", "?"), respond with:
---
👋 **x-ui Deploy** — 自动化在 VPS 上部署 x-ui 代理面板
**功能:**
- 通过 SSH 在 VPS 上一键安装 3x-uiXray 面板)
- 自动配置面板端口、用户名、密码和 Web 路径
- 支持 Debian / Ubuntu / CentOS / AlmaLinux
- 自动开放防火墙端口(ufw / firewalld
- 检测已有安装并支持更新
**执行步骤:**
1. 收集 VPS 信息(host、SSH 用户、密钥路径、面板密码)
2. 验证 SSH 连通性,检查 OS 兼容性
3. 运行 3x-ui 官方一键安装脚本
4. 通过 `x-ui setting` CLI 配置端口/账号/密码/路径
5. 重启服务并验证运行状态
6. 开放防火墙端口
7. 输出面板 URL 和登录信息
**使用示例:**
- `在 198.51.100.42 上部署 x-uiroot 用户,密钥 ~/.ssh/id_rsa`
- `Install 3x-ui on my VPS at vps.example.com, SSH port 2222, user ubuntu`
- `我的 VPS 已经装了 x-ui,帮我更新到最新版本`
---
+11
View File
@@ -0,0 +1,11 @@
{
"name": "xui-deploy",
"description": "Automates installing and configuring x-ui (3x-ui Xray panel) on a VPS via SSH. Use when you want to deploy x-ui, install 3x-ui, set up an Xray proxy panel, or configure inbound proxies on a VPS.",
"prompt": "file://prompts/xui-deploy.md",
"tools": ["execute_bash", "fs_read"],
"allowedTools": ["fs_read"],
"resources": [
"skill://skills/xui-deploy/SKILL.md"
],
"welcomeMessage": "Ready to deploy x-ui on your VPS. Tell me your VPS host, SSH user, and panel password to get started."
}
+49
View File
@@ -0,0 +1,49 @@
# xui-deploy
Automates installing and configuring [3x-ui](https://github.com/MHSanaei/3x-ui) (Xray panel) on a remote VPS over SSH.
## Architecture
![Architecture](assets/xui-deploy-architecture.svg)
## Workflow
![Workflow](assets/xui-deploy-workflow.svg)
## When to Use
Activate this skill when the user wants to:
- Deploy or install x-ui / 3x-ui on a VPS
- Set up an Xray proxy panel
- Update an existing x-ui installation
- Configure panel port, credentials, or base path
Trigger phrases: `deploy x-ui`, `install x-ui`, `setup xui`, `install 3x-ui`, `xray panel`, `部署x-ui`, `安装x-ui`, `搭建xui`, `xray面板`
## How It Works
1. Collects VPS host, SSH credentials, and panel settings from the user
2. Tests SSH connectivity and checks OS compatibility
3. Runs the official 3x-ui one-line installer (handles fresh install and updates)
4. Configures panel port, username, password, and web base path via `x-ui` CLI
5. Restarts the service and verifies it is running
6. Opens the panel port in the system firewall (ufw or firewalld)
7. Reports the panel URL and credentials
## File Structure
```
xui-deploy/
├── SKILL.md # Instructions for the agent
├── evals/
│ └── evals.json # 4 test cases
└── assets/
├── architecture.puml
├── xui-deploy-architecture.svg
├── workflow.puml
└── xui-deploy-workflow.svg
```
## Evals
```bash
python scripts/run_evals.py xui-deploy
```
+382
View File
@@ -0,0 +1,382 @@
---
name: xui-deploy
description: Automates installing and configuring x-ui (Xray panel) on a VPS via SSH. Use when the user wants to deploy x-ui, set up a proxy panel, install 3x-ui, configure Xray on a VPS, or manage inbound proxies. Triggers on phrases like "deploy x-ui", "install x-ui", "setup xui", "install 3x-ui", "xray panel", "部署x-ui", "安装x-ui", "搭建xui", "安装3x-ui", "xray面板", "代理面板".
compatibility: Requires SSH access to a Linux VPS (Debian/Ubuntu/CentOS). curl must be available on the VPS.
metadata:
author: common-skills
version: "1.1"
---
# x-ui Deploy
Automate installing and configuring [x-ui](https://github.com/MHSanaei/3x-ui) (3x-ui fork) on a remote VPS over SSH.
For Xray inbound protocol recommendations and configuration details, see [references/xray-inbound-config.md](references/xray-inbound-config.md).
Accumulated experience, known issues, and proven configurations are in [references/lessons-learned.md](references/lessons-learned.md).
## Experience Base
### Reading (before starting)
Always read `references/lessons-learned.md` before executing any workflow step. Check:
- **VPS 环境备注**: if the target host is listed, apply any noted special handling upfront and skip redundant steps
- **已知问题**: pre-empt known failure modes for the detected OS and install method
- **推荐配置**: offer the user any saved configurations that match their scenario
### Writing (after resolving issues or finding good configs)
After any session where a problem was solved or a good configuration was validated, ask the user:
> "要把这个经验/配置记录到经验沉淀吗?下次部署可以直接复用。"
If yes, append to the appropriate section in `references/lessons-learned.md` following the format in that file. Include: date, environment, symptom, cause, and solution (for issues) or scenario, parameters, and notes (for configs).
## Inputs
Collect from the user before starting:
| Field | Example | Default |
|---|---|---|
| VPS host | `123.45.67.89` or `vps.example.com` | — |
| SSH user | `root` | `root` |
| SSH private key path | `~/.ssh/id_ed25519` | `~/.ssh/id_ed25519` |
| SSH port | `22` | `22` |
| x-ui panel port | `54321` | `54321` |
| x-ui username | `admin` | `admin` |
| x-ui password | (from KeePass or user provides) | — |
| x-ui web base path | `/xui/` | `/xui/` |
**Sensitive credentials (password, username) must be retrieved via `kp-get.sh`**, not typed inline or guessed. Ask the user for the KeePass entry title if not provided.
```bash
PANEL_USER=$(bash /repo/common-skills/tools/kp-get.sh "Entry Title" UserName)
PANEL_PASS=$(bash /repo/common-skills/tools/kp-get.sh "Entry Title" Password)
```
If the user does not have a KeePass entry for this VPS, ask them to provide the credentials directly and remind them to store them in KeePass afterward. Never hardcode or echo credentials in output.
## Workflow
### 1. Test SSH Connectivity
```bash
ssh -i <key_path> -p <ssh_port> -o StrictHostKeyChecking=no -o ConnectTimeout=10 \
<user>@<host> "echo OK && uname -a"
```
If this fails, stop and report the error. Do not proceed.
### 2. Check OS Compatibility
```bash
ssh -i <key_path> -p <ssh_port> <user>@<host> \
"cat /etc/os-release | grep -E '^(ID|VERSION_ID)'"
```
Supported: Debian 9+, Ubuntu 18.04+, CentOS 7+, AlmaLinux 8+. Warn the user if the OS is unsupported but continue if they confirm.
### 3. Detect Existing x-ui Installation
```bash
ssh -i <key_path> -p <ssh_port> <user>@<host> "
if docker ps -a --format '{{.Names}}' 2>/dev/null | grep -qE 'x-ui|3x-ui'; then
echo 'DOCKER' && docker ps -a --format '{{.Names}}\t{{.Status}}' | grep -E 'x-ui|3x-ui'
elif command -v x-ui &>/dev/null; then
echo 'NATIVE' && x-ui version
else
echo 'NOT_INSTALLED'
fi
"
```
Based on the result, **ask the user what to do**:
- **`DOCKER`** — Options: 1) Update config (Step 5) 2) Redeploy from scratch 3) Cancel
- **`NATIVE`** — Options: 1) Update config (Step 5) 2) Redeploy from scratch 3) Cancel
- **`NOT_INSTALLED`** — Proceed to Step 4.
If **Redeploy**, clean up first:
```bash
# Docker cleanup
ssh -i <key_path> -p <ssh_port> <user>@<host> "
docker stop x-ui 2>/dev/null || true
docker rm x-ui 2>/dev/null || true
docker rmi ghcr.io/mhsanaei/3x-ui:latest 2>/dev/null || true
rm -rf ~/x-ui
"
# Native cleanup
ssh -i <key_path> -p <ssh_port> <user>@<host> "
x-ui stop 2>/dev/null || true
systemctl disable x-ui 2>/dev/null || true
rm -f /usr/local/bin/x-ui /usr/local/x-ui/x-ui.db
rm -f /etc/systemd/system/x-ui.service
systemctl daemon-reload
"
```
### 4. Install x-ui
**Prefer Docker**. Fall back to native only if Docker is unavailable or fails.
#### 4a. Docker install (preferred)
> ⚠️ Always use `-p 127.0.0.1:<port>:<port>` — never `--network host`. Host networking exposes the panel port publicly and requires iptables workarounds.
```bash
# Install Docker if missing
ssh -i <key_path> -p <ssh_port> <user>@<host> \
"docker --version 2>/dev/null || curl -fsSL https://get.docker.com | sh"
# Run container with localhost-only port binding
ssh -i <key_path> -p <ssh_port> <user>@<host> "
mkdir -p ~/x-ui/db ~/x-ui/cert
docker run -d \
--name x-ui \
--restart unless-stopped \
-p 127.0.0.1:<panel_port>:<panel_port> \
-v ~/x-ui/db:/etc/x-ui \
-v ~/x-ui/cert:/root/cert \
ghcr.io/mhsanaei/3x-ui:latest
"
```
#### 4b. Native install (fallback)
```bash
ssh -i <key_path> -p <ssh_port> <user>@<host> \
"bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/master/install.sh) <<< $'\n'"
```
If CentOS 7 fails, run `yum install -y epel-release` first and retry.
### 5. Configure Panel Settings
> ⚠️ For Docker: use `/app/x-ui` (the binary), not `x-ui` (the shell wrapper which ignores subcommand args).
> ⚠️ Username and password must be set in a single command — setting them separately fails.
```bash
PANEL_USER=$(bash /repo/common-skills/tools/kp-get.sh "<entry_title>" UserName)
PANEL_PASS=$(bash /repo/common-skills/tools/kp-get.sh "<entry_title>" Password)
# Docker
ssh -i <key_path> -p <ssh_port> <user>@<host> "docker exec x-ui /app/x-ui setting -port <panel_port>"
ssh -i <key_path> -p <ssh_port> <user>@<host> "docker exec x-ui /app/x-ui setting -username '$PANEL_USER' -password '$PANEL_PASS'"
ssh -i <key_path> -p <ssh_port> <user>@<host> "docker exec x-ui /app/x-ui setting -webBasePath <base_path>"
# Native
ssh -i <key_path> -p <ssh_port> <user>@<host> "x-ui setting -port <panel_port>"
ssh -i <key_path> -p <ssh_port> <user>@<host> "x-ui setting -username '$PANEL_USER' -password '$PANEL_PASS'"
ssh -i <key_path> -p <ssh_port> <user>@<host> "x-ui setting -webBasePath <base_path>"
```
### 6. Set Random Subscription Path
Do this immediately after panel config — eliminates the "subscription URI insecure" security warning.
> ⚠️ Only write `subPath`. Do NOT write `subURI` — it causes the settings page to go blank.
```bash
RAND=$(cat /proc/sys/kernel/random/uuid | tr -d '-' | head -c 12)
ssh -i <key_path> -p <ssh_port> <user>@<host> "
docker exec x-ui sqlite3 /etc/x-ui/x-ui.db \
\"INSERT OR REPLACE INTO settings (key, value) VALUES ('subPath', '/$RAND/');\"
"
echo "Subscription path: /$RAND/"
```
### 7. Restart & Verify
```bash
# Docker
ssh -i <key_path> -p <ssh_port> <user>@<host> "docker restart x-ui && sleep 3 && docker ps --filter name=x-ui --format '{{.Status}}'"
# Native
ssh -i <key_path> -p <ssh_port> <user>@<host> "x-ui restart && x-ui status"
```
If not running: `docker logs x-ui --tail 30` or `x-ui log`.
### 8. Restrict Panel Port to Localhost
For **Docker with `-p 127.0.0.1:port:port`**: already localhost-only, skip this step.
For **Docker with `--network host`** or **native install**:
```bash
ssh -i <key_path> -p <ssh_port> <user>@<host> "
sudo iptables -I INPUT -p tcp --dport <panel_port> ! -s 127.0.0.1 -j DROP
# Persist rules
if command -v netfilter-persistent &>/dev/null; then
sudo netfilter-persistent save
else
echo 'WARNING: netfilter-persistent not installed — iptables rule will not survive reboot'
echo 'Install with: sudo apt-get install -y iptables-persistent'
fi
"
```
To access the panel remotely via SSH tunnel:
```bash
ssh -i <key_path> -p <ssh_port> -L <panel_port>:127.0.0.1:<panel_port> <user>@<host> -N
```
### 9. Set Up Nginx Reverse Proxy
Ask the user if they have a domain. If yes, set up HTTPS. If no, set up HTTP-only.
#### 9a. Install Nginx if missing
```bash
ssh -i <key_path> -p <ssh_port> <user>@<host> "
command -v nginx &>/dev/null || (apt-get install -y nginx 2>/dev/null || yum install -y nginx)
sudo systemctl enable --now nginx
"
```
#### 9b. Write config (HTTP first, for certbot validation)
```bash
ssh -i <key_path> -p <ssh_port> <user>@<host> "sudo tee /etc/nginx/sites-available/<domain> > /dev/null << 'EOF'
server {
listen 80;
server_name <domain>;
location <base_path> {
proxy_pass http://127.0.0.1:<panel_port><base_path>;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
}
EOF
sudo ln -sf /etc/nginx/sites-available/<domain> /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx"
```
#### 9c. HTTPS with Let's Encrypt
```bash
ssh -i <key_path> -p <ssh_port> <user>@<host> "
command -v certbot &>/dev/null || apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <domain> --non-interactive --agree-tos -m <admin_email>
"
```
#### 9d. Final Nginx config (HTTPS + panel + WebSocket)
After certbot, update the config to include the WS inbound location:
```nginx
server {
listen 80;
server_name <domain>;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name <domain>;
ssl_certificate /etc/letsencrypt/live/<domain>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<domain>/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location <base_path> {
proxy_pass http://127.0.0.1:<panel_port><base_path>;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location <ws_path> {
proxy_pass http://127.0.0.1:<inbound_port>;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 86400s;
}
}
```
Verify certbot auto-renewal: `sudo certbot renew --dry-run`
### 10. Post-Deploy Security Check
```bash
ssh -i <key_path> -p <ssh_port> <user>@<host> "
echo '=== Panel port exposure ==='
ss -tlnp | grep ':<panel_port>' | grep -v '127.0.0.1' && echo 'WARNING: panel port publicly exposed!' || echo 'OK: localhost-only'
echo '=== Root login ==='
grep '^PermitRootLogin' /etc/ssh/sshd_config || echo 'not explicitly set'
echo '=== Password auth ==='
grep '^PasswordAuthentication' /etc/ssh/sshd_config || echo 'not explicitly set'
"
```
Flag as risks:
- Panel port on `0.0.0.0`/`*`**CRITICAL**: apply iptables rule from Step 8
- `PasswordAuthentication yes` → recommend disabling (key-only auth)
- `PermitRootLogin yes` → recommend `prohibit-password`
### 11. Configure Xray Inbound
Ask the user:
> "要设置 Xray 入站协议吗?推荐 VLESS+WebSocket+TLS(有域名/CDN)或 VLESS+Reality(无域名)。"
Follow [references/xray-inbound-config.md](references/xray-inbound-config.md) for full steps.
**Key notes for this version's API** (confirmed working):
- Login endpoint: `POST <base_path>login`
- Add inbound: `POST <base_path>panel/api/inbounds/add`
- List inbounds: `GET <base_path>panel/api/inbounds/list`
- Use `/app/x-ui` binary inside Docker container, not the `x-ui` shell wrapper
**WS path must be randomized** — never use `/ws/`. Generate a random path:
```bash
WS_PATH="/$(cat /proc/sys/kernel/random/uuid | tr -d '-' | head -c 8)/"
```
After creating the inbound, output the VLESS share link and optionally generate a QR code:
```bash
# Generate QR in terminal (requires qrencode)
echo "vless://..." | qrencode -t ansiutf8
```
### 12. Report Results
- Panel URL: `https://<domain><base_path>`
- SSH tunnel (direct): `ssh -L <panel_port>:127.0.0.1:<panel_port> <user>@<host> -p <ssh_port> -N`
- VLESS share link + QR code
- Certbot renewal status
## Edge Cases
- **Docker `--network host` (legacy/existing)**: Panel port will be on `0.0.0.0`. Apply iptables rule in Step 8. For new deployments always use `-p 127.0.0.1:port:port`.
- **`x-ui setting` shows help instead of applying**: Use `/app/x-ui setting` directly inside Docker container.
- **Username/password setting fails**: Must pass `-username` and `-password` together in one command.
- **Settings page blank after DB edit**: Check for invalid keys — only valid keys are `secret`, `webPort`, `webBasePath`, `subPath`. Delete any others and restart.
- **API 404 on `/xui/API/inbounds`**: Correct path is `<base_path>panel/api/inbounds/add`.
- **Port already in use**: Step 7 shows bind error. Choose different port, re-run Steps 57.
- **CentOS 7**: Run `yum install -y epel-release` before native install.
- **Non-root SSH user**: Prefix commands with `sudo`.
- **Cloud provider security groups (Oracle/AWS/GCP)**: Do not use ufw/firewalld for panel port — manage via cloud console. Use iptables only for localhost restriction.
## Security Notes
- Always retrieve credentials via `kp-get.sh`. Never echo passwords.
- Docker: use `-p 127.0.0.1:port:port` to enforce localhost binding at container level.
- Native: use iptables to block external panel access. Install `iptables-persistent` to survive reboots.
- Randomize WS path and subscription path — never use defaults.
- Enable HTTPS (Step 9) — direct port access only via SSH tunnel.
- `PasswordAuthentication yes` in sshd_config is a risk — recommend key-only.
- Panel port ≠ SSH port.
@@ -0,0 +1,40 @@
@startuml xui-deploy-architecture
skinparam componentStyle rectangle
skinparam defaultFontName Arial
skinparam backgroundColor #FAFAFA
package "xui-deploy Skill" {
component "SKILL.md\n(instructions)" as SKILL
component "evals/evals.json" as EVALS
}
package "Local Machine" {
component "SSH private key" as KEY
actor Developer
}
package "VPS (Linux)" {
component "curl / bash" as CURL
component "x-ui CLI\n(x-ui setting / restart)" as XUCLI
component "3x-ui Service\n(systemd)" as SERVICE
database "x-ui Data\n(/etc/x-ui/)" as DATA
component "Panel Web UI\n(:<panel_port>)" as PANEL
component "Firewall\n(ufw / firewalld)" as FW
}
cloud "GitHub" {
component "install.sh\n(MHSanaei/3x-ui)" as INSTALLER
}
Developer --> SKILL : triggers skill
SKILL --> KEY : SSH auth
SKILL --> CURL : runs installer via SSH
CURL --> INSTALLER : downloads install.sh
INSTALLER --> SERVICE : installs & starts
SKILL --> XUCLI : configure port/user/pass/path
XUCLI --> SERVICE : restart
SERVICE --> DATA : persists config
SERVICE --> PANEL : serves web UI
SKILL --> FW : opens panel port
SKILL --> Developer : reports panel URL
@enduml
+43
View File
@@ -0,0 +1,43 @@
@startuml xui-deploy-workflow
skinparam defaultFontName Arial
skinparam backgroundColor #FAFAFA
actor Developer
participant "xui-deploy\nSkill" as SKILL
participant "VPS (SSH)" as VPS
participant "GitHub\n(install.sh)" as GH
Developer -> SKILL : deploy x-ui on VPS
SKILL -> Developer : collect host, SSH user, key, panel password
SKILL -> VPS : ssh echo OK (connectivity test)
alt SSH fails
SKILL -> Developer : report error, stop
end
SKILL -> VPS : cat /etc/os-release
alt unsupported OS
SKILL -> Developer : warn, ask to confirm
end
SKILL -> VPS : curl install.sh | bash
VPS -> GH : download install.sh
alt x-ui already installed
VPS -> VPS : update to latest version
else fresh install
VPS -> VPS : install 3x-ui + systemd service
end
SKILL -> VPS : x-ui setting -port / -username / -password / -webBasePath
SKILL -> VPS : x-ui restart
SKILL -> VPS : x-ui status
alt service not running
SKILL -> VPS : x-ui log (last 30 lines)
SKILL -> Developer : report error
end
SKILL -> VPS : ufw allow <port> OR firewall-cmd --add-port
SKILL -> Developer : panel URL + username + version
@enduml
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

+25
View File
@@ -0,0 +1,25 @@
{
"skill_name": "xui-deploy",
"evals": [
{
"id": 1,
"prompt": "帮我在 VPS 上部署 x-uiIP 是 198.51.100.42SSH 用 root 登录,密钥在 ~/.ssh/id_rsaKeePass 条目名是 'VPS-xui'",
"expected_output": "Agent uses kp-get.sh to retrieve username and password from KeePass entry 'VPS-xui' before running any commands. Tests SSH connectivity, runs the 3x-ui installer, configures panel settings using the retrieved credentials (never echoing the password), restarts the service, opens the firewall port, and reports the panel URL."
},
{
"id": 2,
"prompt": "I want to install 3x-ui on my Ubuntu 22.04 VPS at vps.example.com. SSH port is 2222, user is ubuntu, key at ~/.ssh/vps_key. Set panel port to 8443 and base path to /panel/. KeePass entry: 'vps.example.com'",
"expected_output": "Agent calls kp-get.sh with entry 'vps.example.com' to get UserName and Password. Uses non-default SSH port 2222, prefixes x-ui commands with sudo (non-root user), sets panel port to 8443 and base path to /panel/, then reports the panel URL as http://vps.example.com:8443/panel/."
},
{
"id": 3,
"prompt": "部署 x-ui,但我的 VPS 已经装过了,想更新到最新版本",
"expected_output": "Agent asks for VPS host, SSH credentials, and KeePass entry title if not provided. Runs the installer which detects the existing installation and updates it. Retrieves credentials via kp-get.sh, restarts the service, verifies it is running, and reports the updated version."
},
{
"id": 4,
"prompt": "在 CentOS 7 的 VPS 上安装 x-uiIP 192.0.2.10root 用户,密钥 ~/.ssh/id_ed25519。我没有 KeePass 条目,密码是 S3cr3t!",
"expected_output": "Agent detects no KeePass entry is available, accepts the password directly, reminds the user to save it to KeePass afterward, and never echoes the password in output. Detects CentOS 7, handles epel-release if needed, uses firewall-cmd to open the panel port, and reports the panel URL."
}
]
}
@@ -0,0 +1,131 @@
# x-ui Deploy — 经验沉淀
本文件记录在实际部署和使用过程中积累的问题解决方案、最佳配置和注意事项。
每次遇到新问题或发现更好的配置时,由 agent 询问用户后追加到对应章节。
---
## 已知问题与解决方案
<!-- 格式:
### [日期] 问题标题
**环境**: OS / 安装方式(Docker/native
**现象**: 描述问题
**原因**: 根本原因
**解决**: 具体命令或步骤
-->
_暂无记录_
---
## 推荐配置
<!-- 格式:
### 配置名称
**适用场景**: 什么情况下使用
**配置内容**: 具体参数或命令
**备注**: 注意事项
-->
_暂无记录_
---
## VPS 环境备注
<!-- 记录特定 VPS 的特殊情况,如端口限制、ISP 封锁、已知可用配置等 -->
<!-- 格式:
### <host/domain>
- 系统: Ubuntu 22.04
- 安装方式: Docker
- 可用协议: VLESS+WS+TLS
- 特殊情况: ...
-->
_暂无记录_
---
## 已知问题与解决方案
### [2026-04-25] x-ui setting 子命令参数不生效(Docker 容器)
**环境**: Ubuntu 20.04 aarch64 / Docker (ghcr.io/mhsanaei/3x-ui:latest)
**现象**: `docker exec x-ui x-ui setting -port 54321` 显示帮助菜单而非执行设置
**原因**: 容器内 `x-ui` 是 shell 脚本包装器,需直接调用 `/app/x-ui` 二进制
**解决**:
```bash
docker exec x-ui /app/x-ui setting -port 54321
docker exec x-ui /app/x-ui setting -username 'user' -password 'pass' # 用户名和密码必须同时传入
docker exec x-ui /app/x-ui setting -webBasePath /xui/
```
### [2026-04-25] x-ui panel API 路径与文档不符
**环境**: Ubuntu 20.04 / Docker (ghcr.io/mhsanaei/3x-ui:latest)
**现象**: `/xui/API/inbounds/add` 返回 404
**原因**: 该版本实际 API 路径为 `/xui/panel/api/inbounds/add`(含 base_path 前缀)
**解决**:
```bash
# 正确路径(base_path=/xui/
POST http://127.0.0.1:54321/xui/panel/api/inbounds/add
GET http://127.0.0.1:54321/xui/panel/api/inbounds/list
```
### [2026-04-25] 写入错误的 settings 字段导致面板设置页空白
**环境**: Ubuntu 20.04 / Docker
**现象**: 向 settings 表写入 `subURI` 字段后,面板设置页面内容消失
**原因**: `subURI` 不是有效字段名,导致前端渲染异常
**解决**: 删除无效字段并重启
```bash
docker exec x-ui sqlite3 /etc/x-ui/x-ui.db "DELETE FROM settings WHERE key='subURI';"
docker restart x-ui
```
**正确字段**: 订阅路径只需写入 `subPath`
### [2026-04-25] --network host 模式导致面板端口对外暴露
**环境**: Ubuntu 20.04 / Docker
**现象**: 容器以 `--network host` 启动,`ss -tlnp` 显示 54321 监听在 `*:54321`
**原因**: host 网络模式绕过了 Docker 端口绑定,无法用 `-p 127.0.0.1:port:port` 限制
**解决**: 用 iptables 封锁外部访问
```bash
sudo iptables -I INPUT -p tcp --dport 54321 ! -s 127.0.0.1 -j DROP
sudo netfilter-persistent save # 持久化(如已安装)
```
---
## 推荐配置
### VLESS + WebSocket + TLS + Cloudflare CDN(中国大陆优化)
**适用场景**: 有域名、域名托管在 Cloudflare、中国大陆用户
**配置内容**:
- Nginx 监听 443,反向代理面板(/xui/)和 WS 入站(/ws/
- x-ui inbound: VLESS, listen 127.0.0.1:10000, WS path /ws/
- Cloudflare: 橙云代理开启,SSL 模式 Full(strict)WebSocket 开启
- 客户端: 地址填 CF 优选 IP,SNI 和 Host 填域名
**备注**: CF 免费版 WebSocket 有并发限制;优选 IP 用 CloudflareSpeedTest 工具测速
### 订阅路径安全配置
**适用场景**: 消除面板安全警告"订阅默认 URI 路径不安全"
**配置内容**:
```bash
RAND=$(cat /proc/sys/kernel/random/uuid | tr -d '-' | head -c 12)
docker exec x-ui sqlite3 /etc/x-ui/x-ui.db "INSERT OR REPLACE INTO settings (key, value) VALUES ('subPath', '/$RAND/');"
docker restart x-ui
```
**备注**: 只写 `subPath`,不要写 `subURI`(会导致设置页空白)
---
## VPS 环境备注
### 168.138.188.177 (proxy.157301.xyz)
- **系统**: Ubuntu 20.04 aarch64
- **云平台**: Oracle Cloud 新加坡
- **SSH**: 端口 2222,用户 ubuntu,密钥 ~/.ssh/id_ed25519
- **安装方式**: Docker (--network host)
- **面板**: https://proxy.157301.xyz/xui/ (端口 54321iptables 封锁外部访问)
- **可用协议**: VLESS+WS+TLSWS 路径 /ws/,经 Nginx 代理
- **CDN**: Cloudflare 橙云已开启
- **防火墙**: Oracle Cloud 安全组管理,不使用 ufw
- **KeePass 条目**: x-ui
@@ -0,0 +1,164 @@
# Recommended Xray Inbound Configuration
This reference covers the best protocol/transport combinations for security and performance,
and the API payloads to create them via the x-ui REST API.
## Protocol Recommendation (ranked)
| Rank | Protocol | Transport | TLS | Why |
|------|----------|-----------|-----|-----|
| ✅ Best | VLESS | WebSocket | TLS (via Nginx) | CDN-friendly, low overhead, widely supported |
| ✅ Best | VLESS | gRPC | TLS (via Nginx) | Multiplexed, low latency, CDN-friendly |
| Good | VLESS | TCP | XTLS/Reality | No CDN needed, excellent performance, anti-detection |
| Good | VMess | WebSocket | TLS (via Nginx) | Broad client support |
| Avoid | VMess | TCP | none | Detectable, no forward secrecy |
| Avoid | Shadowsocks | — | — | Blocked in many regions |
**Default recommendation**: VLESS + WebSocket + TLS, proxied through Nginx.
- Panel port stays on `127.0.0.1` (localhost-only)
- Nginx terminates TLS on 443 and forwards to the inbound port on `127.0.0.1`
- Inbound port also stays localhost-only (e.g. `127.0.0.1:10000`)
---
## Step-by-Step: Create VLESS + WS + TLS Inbound
### 1. Collect inbound parameters
Ask the user (or use defaults):
| Field | Default |
|-------|---------|
| Inbound port | `10000` |
| WS path | `/ws/` (use a random string for security) |
| Remark | `vless-ws` |
### 2. Create inbound via x-ui API
The x-ui panel exposes a REST API at `http://127.0.0.1:<panel_port><base_path>`.
Authenticate with a session cookie first, then POST the inbound.
```bash
# 1. Login and save session cookie
PANEL_USER=$(bash /repo/common-skills/tools/kp-get.sh "<entry_title>" UserName)
PANEL_PASS=$(bash /repo/common-skills/tools/kp-get.sh "<entry_title>" Password)
ssh -i <key_path> -p <ssh_port> <user>@<host> "
curl -sc /tmp/xui-cookie.txt \
-X POST http://127.0.0.1:<panel_port><base_path>login \
-H 'Content-Type: application/json' \
-d '{\"username\":\"'\"$PANEL_USER\"'\",\"password\":\"'\"$PANEL_PASS\"'\"}'
"
# 2. Generate a UUID for the client
UUID=$(ssh -i <key_path> -p <ssh_port> <user>@<host> "cat /proc/sys/kernel/random/uuid")
# 3. Create VLESS + WebSocket inbound
ssh -i <key_path> -p <ssh_port> <user>@<host> "
curl -sb /tmp/xui-cookie.txt \
-X POST http://127.0.0.1:<panel_port><base_path>xui/API/inbounds/add \
-H 'Content-Type: application/json' \
-d '{
\"remark\": \"vless-ws\",
\"enable\": true,
\"protocol\": \"vless\",
\"listen\": \"127.0.0.1\",
\"port\": 10000,
\"settings\": \"{\\\"clients\\\":[{\\\"id\\\":\\\"'\"$UUID\"'\\\",\\\"flow\\\":\\\"\\\"}],\\\"decryption\\\":\\\"none\\\"}\",
\"streamSettings\": \"{\\\"network\\\":\\\"ws\\\",\\\"wsSettings\\\":{\\\"path\\\":\\\"/ws/\\\"}}\",
\"sniffing\": \"{\\\"enabled\\\":true,\\\"destOverride\\\":[\\\"http\\\",\\\"tls\\\"]}\"
}'
"
```
### 3. Add Nginx location for the WS path
Append to the existing Nginx config (`/etc/nginx/conf.d/x-ui.conf`):
```nginx
location /ws/ {
proxy_pass http://127.0.0.1:10000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 86400s;
}
```
Reload Nginx: `systemctl reload nginx`
### 4. Report client config
Output the connection info for the user to import into their client (v2rayN, Clash, etc.):
```
Protocol : VLESS
Address : <domain>
Port : 443
UUID : <uuid>
Transport: WebSocket
Path : /ws/
TLS : TLS
SNI : <domain>
```
Or generate a VLESS share link:
```
vless://<uuid>@<domain>:443?encryption=none&security=tls&type=ws&path=%2Fws%2F&sni=<domain>#vless-ws
```
---
## Alternative: VLESS + Reality (no CDN, no domain needed)
Reality is the best option when no domain is available. It mimics a real TLS handshake against a target site.
```bash
# Generate Reality key pair inside the container/host
ssh -i <key_path> -p <ssh_port> <user>@<host> \
"docker exec x-ui xray x25519" # Docker
# or: xray x25519 # native
```
Save the `Private key` and `Public key` output.
Create inbound via API (replace keys and port):
```json
{
"remark": "vless-reality",
"enable": true,
"protocol": "vless",
"listen": "",
"port": 443,
"settings": "{\"clients\":[{\"id\":\"<uuid>\",\"flow\":\"xtls-rprx-vision\"}],\"decryption\":\"none\"}",
"streamSettings": "{\"network\":\"tcp\",\"security\":\"reality\",\"realitySettings\":{\"show\":false,\"dest\":\"www.microsoft.com:443\",\"serverNames\":[\"www.microsoft.com\"],\"privateKey\":\"<private_key>\",\"shortIds\":[\"\"]}}"
}
```
Client connection info:
```
Protocol : VLESS
Address : <vps_ip>
Port : 443
UUID : <uuid>
Flow : xtls-rprx-vision
Transport : TCP
Security : Reality
PublicKey : <public_key>
SNI : www.microsoft.com
```
> Note: Reality listens on `0.0.0.0:443` (must be public). This is intentional — it's the proxy traffic port, not the panel.
---
## Security Hardening for Inbounds
- Always set `"listen": "127.0.0.1"` for WS/gRPC inbounds (Nginx handles public exposure).
- Use a random UUID per client; rotate periodically.
- Use a non-obvious WS path (e.g. `/a3f9k2/` not `/ws/`).
- Enable sniffing (`destOverride: ["http","tls"]`) to block DNS leaks.
- For Reality, use a high-traffic legitimate domain as `dest` (e.g. `www.microsoft.com`, `www.apple.com`).