适用场景
代码仓库在远端服务器上、本地笔记本性能有限,或者远端挂着 GPU 需要跑训练与推理,日常又想在自己熟悉的图形编辑器里写代码——这类场景适合把本地轻量客户端接到远端开发机。本文的做法是:本地只负责界面和编辑,代码、依赖、数据、终端全部留在远端,避免把几十 GB 的仓库和数据集往本地拉。
环境与前置条件
本地
- 操作系统:macOS、Windows 10/11、主流 Linux 桌面发行版均可
- 已安装 Cursor 客户端,版本以官方文档当前版本为准;本地与远端的组件版本需要匹配,版本差太多时连接会提示重新部署远端组件
- 磁盘:本地留出 5GB 以上给客户端缓存和日志
远端
- Linux 服务器(Ubuntu / Debian / CentOS / Rocky 等常见发行版),开启了 OpenSSH 服务端
- 一个可登录的普通用户,对工作目录有读写权限;
sudo权限可选,用于装系统依赖 - 磁盘:剩余空间至少能放下代码仓库、虚拟环境和缓存,建议 50GB 起步;大仓库场景按仓库体量再放大
- 内存:一般开发 8GB 起;编译、跑数据处理的场景建议 16GB 起;GPU 场景按模型实际显存需求准备,显存与内存是两回事
- 网络:本地能通过 SSH 直连远端,或者经跳板机、端口转发、内网穿透访问。远端建议能访问外网,便于自动下载远端组件
你需要提前问清楚的
远端主机的地址或别名、SSH 端口、登录用户名、是否需要跳板机、工作目录放在哪个盘。
分步骤部署
第 1 步:在远端准备一个专用工作区
先在远端建目录,避免直接用 / 或别人已经占用的路径。
```bash
登录远端
ssh user@remote-host
建一个自己的空间(以 /srv/workspace 为例,可换成任何有写权限的路径)
sudo mkdir -p /srv/workspace
sudo chown -R "$USER":"$USER" /srv/workspace
ls -ld /srv/workspace
```
成功标志:ls -ld 输出的属主和属组都是你自己的用户名。如果不是,后面的文件保存会出现只读报错。
顺手确认 SSH 目录权限,这是后面配对成功的前提:
```bash
mkdir -p ~/.ssh
chmod 700 ~/.ssh
```
第 2 步:本地生成密钥并完成首次配对
所谓“配对”,本质是两件事:本地生成一对密钥,把公钥装到远端的 authorized_keys;第一次连接时确认远端主机指纹。这两步做完,后续连接就不再需要密码。
```bash
生成一对专用密钥(不要和日常用的混在一起)
ssh-keygen -t ed25519 -C "cursor-remote" -f ~/.ssh/id_ed25519_cursor
把公钥推到远端
ssh-copy-id -i ~/.ssh/id_ed25519_cursor.pub user@remote-host
```
执行 ssh-copy-id 时会提示输入一次远端密码。输完之后手动连一次:
```bash
ssh -i ~/.ssh/id_ed25519_cursor user@remote-host 'echo pair-ok'
```
第一次连接会打印一段指纹,并问 Are you sure you want to continue connecting (yes/no)?,输入 yes。看到 pair-ok 就说明配对完成。
Windows 上没有 ssh-copy-id 时,可以手动追加公钥:
```bash
type $env:USERPROFILE\.ssh\id_ed25519_cursor.pub | ssh user@remote-host "cat >> ~/.ssh/authorized_keys"
```
第 3 步:把连接参数写进 SSH config
每次都敲一长串参数容易错,写进配置文件后,客户端和命令行都能用别名连接。
```bash
本地执行
cat >> ~/.ssh/config <<'EOF'
Host gpu-box
HostName 10.0.0.12
User devuser
Port 22
IdentityFile ~/.ssh/id_ed25519_cursor
IdentitiesOnly yes
ServerAliveInterval 30
ServerAliveCountMax 6
Compression yes
ForwardAgent no
EOF
chmod 600 ~/.ssh/config
```
几个字段的实际作用:
IdentitiesOnly yes:只尝试指定的这把密钥,避免密钥太多导致Too many authentication failuresServerAliveInterval与ServerAliveCountMax:每 30 秒发一次心跳,共 6 次无响应才断开,缓解长时间空闲被防火墙掐断的问题ForwardAgent no:不把本地 agent 转发到远端,减少密钥被远端进程滥用的风险Compression yes:网络带宽紧张时对小文件传输有帮助
需要经跳板机时,再加一行 ProxyJump jump-host,跳板机本身也写成一段 Host 配置即可。
改完验证:
```bash
ssh gpu-box 'hostname && whoami'
```
第 4 步:在客户端里发起远程连接
打开命令面板(macOS 为 Cmd+Shift+P,Windows / Linux 为 Ctrl+Shift+P),输入 Remote 过滤,选择连接远程主机的命令,然后在列表里选中上一步配好的 gpu-box。
具体命令名称和界面文案会随版本演进,以你本地版本的实际界面和官方文档为准。这一步在做什么:客户端通过 SSH 登录远端,把一份轻量的远端组件部署到远端用户目录,然后由这个组件负责文件读写、终端和语言服务。首次连接通常需要几十秒到几分钟,界面会显示部署进度。
成功标志:窗口左下角或状态栏显示 gpu-box 之类的主机标识,而不是本机名称。
第 5 步:打开远端目录
选择“打开文件夹”,在输入框里填远端绝对路径,例如 /srv/workspace/my-project。此时左侧文件树里的内容全部来自远端,本地磁盘上并没有这份代码。
如果这一步提示路径不存在,先在远程终端里 ls /srv/workspace 确认目录名大小写和层级。
第 6 步:打开远程终端并确认环境
用 Ctrl+ (反引号)打开集成终端。重点看终端面板的标题栏是否标注了远端主机名——不标注时很容易误在本机执行命令。
```bash
uname -a
hostname
whoami
pwd
nvidia-smi # GPU 机器上执行,能看到显卡列表说明环境对了
```
长时间跑的任务不要挂在集成终端里,关掉窗口就可能被中断。用 tmux 托管:
```bash
tmux new -s dev
在里面跑训练或编译
按 Ctrl+b 再按 d 脱离
tmux attach -t dev # 下次回来
tmux ls # 看有哪些会话
```
第 7 步:文件同步策略
大仓库场景下,建议以远端为唯一真实来源,本地编辑器直接读写远端文件,不做整仓复制。只有在需要本地留一份副本(比如离线看代码、本地跑测试)时才用增量同步。
把远端拉到本地:
```bash
rsync -avz --delete \
--exclude '.git' \
--exclude 'node_modules' \
--exclude '__pycache__' \
--exclude '*.pt' \
--exclude '*.ckpt' \
gpu-box:/srv/workspace/my-project/ ~/work/my-project/
```
把本地推到远端:
```bash
rsync -avz --exclude '.git' --exclude 'node_modules' \
~/work/my-project/ gpu-box:/srv/workspace/my-project/
```
两条注意事项:
--delete会删掉目标端多余的文件,第一次用先加-n(dry run)看一遍输出再真正执行。- 不要两端同时改同一个文件。rsync 是单向覆盖,不做冲突合并。要双向同步就用 unison 之类的工具,并且约定好改动方向。
也可以把远端目录挂载到本地:
```bash
mkdir -p ~/mnt/gpu
sshfs gpu-box:/srv/workspace ~/mnt/gpu -o reconnect,ServerAliveInterval=15
```
适合浏览小文件,大仓库下索引和 IO 会明显变慢,不建议用这种方式跑训练相关的工作。
第 8 步:指定远端解释器
在编辑器里选择解释器时,选远端路径,例如 /srv/workspace/my-project/.venv/bin/python。用 conda 的话先在远程终端激活环境再查路径:
```bash
conda activate myenv
which python
```
把输出的路径填到解释器选择框里。这一步做对,代码补全、运行、调试才会走远端环境,而不是本机那个没装依赖的解释器。
验证部署是否成功
按顺序跑一遍,全过就说明链路通了:
1. 远程终端里 hostname 输出的是远端主机名,不是本地机器名。
2. 远程终端里 pwd 在你打开的工作区目录下。
3. GPU 机器上 nvidia-smi 能列出显卡。
4. 在编辑器里改一行代码并保存,然后在远程终端执行 tail -n 3 该文件,看到刚改的内容——说明编辑写的是远端文件。
5. 跑仓库里一条最轻的检查命令,例如:
```bash
python -c "import sys; print(sys.executable)"
python -c "import torch; print(torch.cuda.is_available())"
```
第一条输出的路径应当是远端解释器路径;第二条 GPU 场景下应为 True。
6. 关闭窗口后重新连接到 gpu-box,再打开同一个目录,确认工作区能恢复、文件内容是最新的。
常见报错与解决
报错:Permission denied (publickey).
原因:公钥没有写进远端的 authorized_keys,或者远端 .ssh 目录、文件的权限过宽,被 sshd 拒绝使用。
```bash
远端执行
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
cat ~/.ssh/authorized_keys # 确认里面有本地 id_ed25519_cursor.pub 的内容
本地重新推送公钥
ssh-copy-id -i ~/.ssh/id_ed25519_cursor.pub user@remote-host
```
报错:WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
原因:远端重装了系统、换了密钥,或者同 IP 被分配给了别的机器,本地 known_hosts 里记录的指纹对不上。
```bash
本地执行,把旧记录删掉再连
ssh-keygen -R gpu-box
ssh gpu-box 'echo ok'
```
确认新指纹来源可靠后再输 yes。
报错:连接建立后几秒到几分钟自动断开,或报 Connection timed out
原因:中间有防火墙或 NAT 会话超时,也可能远端 sshd 不在默认端口上。
```bash
本地探测端口是否可达
nc -vz 10.0.0.12 22
远端确认 sshd 监听端口
ss -tlnp | grep sshd
```
端口不对就在 SSH config 里改 Port;是空闲超时就在 config 里加 ServerAliveInterval 30 和 ServerAliveCountMax 6;跨网络不通时改用跳板机配 ProxyJump。
报错:远端组件反复重新部署,或提示下载失败
原因:远端访问外网受限,或者用户目录磁盘写满。
```bash
远端执行
df -h ~
du -sh ~/.cursor-server 2>/dev/null
```
空间不足就清理旧日志和缓存;网络受限则按官方文档配置代理,或使用官方提供的离线安装方式,具体以官方文档当前版本为准。
报错:保存文件提示只读,或 Write failed / No space left on device
原因:远端用户对目录没有写权限,或磁盘真的满了。
```bash
远端执行
ls -ld /srv/workspace/my-project
sudo chown -R "$USER":"$USER" /srv/workspace/my-project
df -h
du -sh ~/.cursor-server/* 2>/dev/null | sort -h | tail
```
后续维护
备份
- 代码用远端 Git 仓库托管,提交推到远端 origin,本地副本不作为唯一备份
- 数据、模型权重、配置文件单独
rsync到一个备份路径,脚本化并挂定时任务,例如每天凌晨跑一次
```bash
rsync -avz --delete /srv/workspace/data/ /srv/backup/data/
```
- 远端组件目录
~/.cursor-server不需要备份,重连会重新部署
升级
- 本地客户端升级后,远端组件版本可能不匹配,重新连接时通常会有提示,按提示重新部署即可
- 升级前先在 tmux 里保存好正在跑的任务,避免升级过程打断
- 具体的版本兼容矩阵以官方文档当前版本为准
日志与监控
```bash
远端组件日志,路径随版本略有差异,一般在用户目录下
ls -lt ~/.cursor-server/ 2>/dev/null | head
SSH 登录记录
sudo journalctl -u sshd -n 50 --no-pager
资源占用
htop
nvidia-smi -l 5
df -h
```
客户端侧的日志在输出面板里,选远端相关通道查看,连接问题优先看这里。
安全与清理
- 远端
sshd_config里设置PasswordAuthentication no,只留密钥登录,改完sudo systemctl restart sshd - 限制来源 IP 或只允许经跳板机访问,不要把 SSH 端口直接暴露在公网
- 私钥只放本地,权限保持
600;换人换机时轮换密钥并从authorized_keys里删掉旧公钥 - 定期清理不再使用的 tmux 会话和远端缓存目录,避免磁盘被慢慢吃满
界面上的命令名称、菜单位置和远端组件路径会随版本变化,遇到对不上的地方,以官方文档当前版本的说明为准;本文中的 SSH、rsync、tmux 命令是通用写法,可以直接照抄。
