第一次在离网 Linux 服务器上搭 VSCode Remote-SSH 时,我差点被那句Downloading VS Code Server卡到怀疑人生。SSH 能通、密码能输、远端目录也正常,可 VSCode 就是一直提示下载,最后等来一个连接失败。后面我把整套离线部署流程跑通,又给同事批量装过好几台机器,下面这些内容就是完整记录:从 Remote-SSH 的下载机制、离线方案选型,到分步操作、扩展与依赖处理,再到常见报错和批量维护。适合需要在一台无法访问互联网的 Linux 服务器上用 VSCode 远程开发的人,尤其是内网测试机、离线研发环境这类场景。
先说一句大实话:VSCode Remote-SSH 能做的事,本质上是把本地的编辑器和远端的服务连接起来,但远端并不是只有你的代码文件,它还需要一个专门的运行环境。搞清楚这一点,后面所有操作就都顺理成章了。
1. 先搞明白 Remote-SSH 到底卡在哪一步
1.1 vscode-server 不是可有可无的组件
Remote-SSH 的架构不像普通的文件同步工具。你在本地打开一个 VSCode 窗口,它只是壳;远程主机上必须跑一个叫 vscode-server 的服务,这个服务负责文件监听、语法分析、扩展宿主、终端会话和调试器。本地客户端和远端服务之间通过既有的 SSH 连接通信,不需要额外开放一个监听端口。换句话说,SSH 只是管道,vscode-server 才是远端真正干活的服务。
所以,离线环境里你不能只保证 SSH 通。VSCode 客户端在初次连接时会去检查远端用户目录下有没有匹配的 server。没有,就尝试从官方下载服务器;有,就直接启动。这个检查路径是固定的,大致是~/.vscode-server/bin/<commit-id>。commit-id 就是你本地 VSCode 对应的那串版本提交标识。如果你的服务器上已经因为某次失败操作留下了一个坏掉的目录,客户端同样可能一直卡住。
1.2 默认下载失败是必然的
当远端机器无法访问互联网时,客户端发起的下载请求要么超时,要么被拒。你会在 Remote-SSH 的输出日志里看到这一类信息:
[error] Downloading VS Code Server failed. Please try again later. [error] Failed to download VS Code Server. [error] Error: connect ETIMEDOUT 20.xxx.xxx.xxx:443这个阶段很多人会去折腾网络配置,其实方向错了。既然客户端检查的是某个固定目录下的固定文件,那我们完全可以绕过自动下载,手动把 server 包放到它该在的位置。后面所有步骤都是在做这件事。
注意:Remote-SSH 能通的另一个前提是,你的电脑和目标服务器之间本来就要有一条可用的 SSH 链路。离线环境只影响目标服务器访问互联网,不影响局域网内两台机器互访。如果 SSH 本身都不通,Remote-SSH 无论如何也起不来。
2. 离线方案的整体思路和取舍
2.1 核心思路:把 server 包当成普通安装包去搬运
手动部署的思路不复杂:
- 在任意一台能上网的电脑上下载与本地 VSCode 完全同版本、同平台的 vscode-server 压缩包。
- 把这个包通过 U 盘、scp、rsync 等任意方式传到目标机。
- 在目标机上解压到
~/.vscode-server/bin/<commit-id>目录。 - 回到 VSCode 重新连接,客户端发现 server 已经存在,就不会再触发下载。
这个方案能成立的关键,是 VSCode 客户端判断“远端服务是否就绪”时只看固定路径和 commit-id。只要这两个条件满足,它就不会再去请求官方下载站点。所以离线部署不是要改 Remote-SSH 的代码,而是把自动下载变成手动投递。
2.2 为什么不直接用浏览器版方案
有人会推荐在服务器上装一个浏览器访问的 code-server,把它当 VSCode 用。这套方案也可以离线跑,但它和你熟悉的桌面版 VSCode 并不是完全一样的体验:多窗口管理、部分快捷键、调试器 UI、某些只认 Remote 类型的扩展,在浏览器里表现有差异。如果你团队已经习惯了 Remote-SSH 的工作流,做离线部署时尽量保持原生 Remote-SSH 方案,后面恢复联网也不用切来切去。
Remote-SSH 方案还有一个好处:一旦把 vscode-server 放好,后续你做的本地调试、端口转发、终端复用,都和在线环境一样,没有额外学习成本。server 压缩包一般几十兆到一百兆,按版本归档后一次性投入,长期受益。
3. 离线部署实操,按步骤抄就行
3.1 锁定本地 VSCode 的 commit id
第一步是拿到你本地 VSCode 的完整版本号和 commit id。最直接的方式是在本机命令行执行:
code --version输出会包含三行,大概长这样:
1.86.2 e180c05f7f13 x64第二行e180c05f7f13就是 commit id。如果你电脑上code命令不在 PATH 里,也可以打开 VSCode 的Help -> About对话框,界面里同样有这条信息。这里有个容易忽略的点:Remote-SSH server 的版本与本地客户端是强绑定的,理论上一个本地 VSCode 版本对应一个 commit id。如果团队里有人用 1.86.2,有人用 1.87.1,那么同一台远端服务器上~/.vscode-server/bin下就要同时放两个不同 commit-id 的目录。所以第一批部署时,最好先把所有人的本地 VSCode 锁在同一版本,不然每多一个版本就要多搬一次。
3.2 在联网电脑上下载 server 包
拿到 commit id 后,去任意一台能上网的电脑上下载对应的 Linux server 压缩包。官方下载地址的规则是:
curl -L "https://update.code.visualstudio.com/commit:e180c05f7f13/server-linux-x64/stable" -o vscode-server.tar.gzserver-linux-x64里的“x64”要根据目标机架构替换:
| 目标机架构 | URL 中的路径片段 |
|---|---|
| Linux x64 | server-linux-x64 |
| Linux ARM64 | server-linux-arm64 |
| Linux ARM 32 位 | server-linux-armhf |
下载完成后最好看一眼文件大小。正常应该在几十兆到一百多兆之间,如果只有几 KB,大概率是 URL 拼错了,用文本编辑器打开那个文件,里面往往是一段错误提示。
如果联网电脑和离线服务器之间的网络路径也不通,就用 U 盘这类介质传递。只要最终能把 tar.gz 放到服务器上就行,过程不限。
3.3 把包传到目标机
假设服务器地址和用户是user@10.0.0.5,最简单的传法是 scp:
scp -P 22 vscode-server.tar.gz user@10.0.0.5:/tmp/如果端口不是 22,就把-P 22换成实际端口。大文件用 rsync 更稳,中途断了还能断点续传:
rsync -avP vscode-server.tar.gz user@10.0.0.5:/tmp/传完后我会顺手在服务器上确认一下 md5 或者文件大小,防止传到一半损坏。服务器上直接执行:
ll /tmp/vscode-server.tar.gz看到大小和本机一致,再进行下一步。
3.4 在目标机解压到正确位置
登录服务器,用登录用户自己的身份操作,不要一上来就 sudo。关键命令如下:
ssh user@10.0.0.5 mkdir -p ~/.vscode-server/bin/e180c05f7f13 tar -xzf /tmp/vscode-server.tar.gz -C ~/.vscode-server/bin/e180c05f7f13 --strip-components=1这里的--strip-components=1非常关键。如果你少了这个参数,解压后会在bin/e180c05f7f13下多出一层vscode-server-linux-x64子目录,VSCode 客户端按固定路径找server.sh时找不到,还是会重新触发下载。
解压完成后检查一下目录结构:
ls ~/.vscode-server/bin/e180c05f7f13正常情况下应该能看到bin、node、out、package.json、server.sh这些内容。如果在bin/e180c05f7f13下看到vscode-server-linux-x64这个文件夹,说明你没加--strip-components=1,重新解压一遍就好。
还有一个容易踩的坑:如果这个压缩包是在 Windows 上解压过再重新打包的,Linux 上的可执行权限可能会丢。遇到启动失败先跑一下:
chmod +x ~/.vscode-server/bin/e180c05f7f13/server.sh3.5 重新连接并确认
回到本地 VSCode,在 Remote Explorer 里重新连接目标主机。如果一切正常,状态栏不会出现Downloading VS Code Server,而是直接进入远程窗口。这时你随便打开一个文件,看看右下角是否出现“Remote”连接提示,基本就说明 server 已经跑起来了。
如果刚才连到一半失败,服务器上可能残留一个半死不活的进程。不要直接重试,先在 VSCode 命令面板里执行Remote-SSH: Kill VS Code Server on Host,然后再重连。这样能确保下一次启动是干净状态。
4. 离线环境下的扩展与语言服务
4.1 远程扩展才是真正跑在 server 上的那一半
VSCode 扩展里有一部分是界面增强类,运行在本地;另一部分是语言服务、调试器、格式化工具,在远程模式下是运行在服务器上的。比如 Python 扩展的语言服务器,在远程窗口里启动时,实际上是 vscode-server 在跑它。这个区别在离线环境里特别重要:你在本地把扩展装得再全,远程窗口里该装的那份也不会自动出现。
所以离线环境要提前准备两部分:一是 vscode-server 本身,二是远程扩展的安装包。两者都要像搬安装包一样,提前搬到目标机。
4.2 用 .vsix 离线装扩展
扩展市场里每个扩展都有对应的.vsix安装包。在一台能上网的电脑上打开扩展市场网页,搜索你需要的扩展,进入详情页后一般能直接下载.vsix文件。拿到文件后传到目标机:
scp python.vsix user@10.0.0.5:/tmp/然后有两种安装方式。第一种是在 Remote-SSH 远程窗口里执行命令面板的Extensions: Install from VSIX,选择本地或远端路径,VSCode 会把它安装到远端扩展目录。第二种是直接在目标机上用 vscode-server 自带的命令行工具装:
~/.vscode-server/bin/e180c05f7f13/bin/code-server --install-extension /tmp/python.vsix这两种方式的效果一样,都是把扩展放到远端服务器的~/.vscode-server/extensions下。我更喜欢第二种,因为不依赖 VSCode 图形界面,适合批量操作。
4.3 离线依赖才是后半天时间
扩展装好之后,真正写代码还需要解释器和第三方库。Python 项目如果机器没有外网,pip 装包会失败,所以要在联网机器上把 wheel 包提前下载好。一个通用姿势是用 pip download:
pip download -r requirements.txt -d wheels \ --platform manylinux_2_17_x86_64 \ --only-binary=:all: \ --python-version 3.11把--platform和--python-version换成目标机的实际架构和 Python 版本,然后整个wheels目录拷到目标机,在目标机上执行:
pip install --no-index --find-links=/tmp/wheels -r requirements.txtNode 项目如果node_modules实在没法通过内网源装,一个笨但有效的办法是在同架构的联网机器上把依赖装好,再把整个目录拷过去。条件允许的话,尽量用.deb、.whl、.tar.xz这类官方安装包离线安装,比直接拷安装目录更不容易出问题。
5. 常见问题与排查
5.1 一直卡在 Downloading
绝大多数情况是目录没放对,或者 commit id 写错。先打开 Remote-SSH 的输出日志,看它到底想下载哪个 commit-id,然后去服务器上核对:
ls ~/.vscode-server/bin/如果列表里没有日志里那个 commit-id,说明版本对不上。如果有目录,再看目录下有没有server.sh,没有就说明多了一层目录结构。
5.2 SSH 能通,VSCode 就是连不上
Remote-SSH 本质上就是个 SSH 客户端,所以先用命令行单独测一遍:
ssh user@10.0.0.5如果命令行 SSH 都不通,问题在账号、密钥、端口或跳板机配置,和离线 server 无关。如果命令行能进,VSCode 还是失败,重点看 Remote-SSH 日志里的 server 启动过程,确认 server 进程有没有被系统杀掉,以及报错信息里提到的文件路径。
5.3 报 node 相关 so 文件找不到
目标系统太旧时,新版 vscode-server 的 node 会报类似GLIBC_2.xx not found的错误。这种情况一般是因为系统基础库版本不够。经验解法是换一个旧版本的 VSCode 客户端,因为老版本对应老 server,对系统底包的要求会低一些;或者把开发环境放到一个满足要求的容器或虚拟环境里跑。
5.4 权限问题导致连接失败
如果~/.vscode-server是用 root 建的,普通用户连接时经常出现Permission denied。解决方法是删除或 chown,让登录用户拥有这个目录:
sudo chown -R user:user ~/.vscode-server之后的 mkdir、解压、安装扩展,都用登录用户身份操作,不要习惯性加 sudo,否则很容易留下一堆 root 属主的文件。
把上面这些整理成一张速查表,用的时候直接对照:
| 现象 | 大概率原因 | 处理方向 |
|---|---|---|
| 一直 Downloading VS Code Server | server 目录不存在,或路径多一层 | 核对日志里的 commit-id 和server.sh位置 |
| 连接后立刻退出 | server 权限丢失或系统库过旧 | 重新 chmod,或换旧版 VSCode |
| Permission denied | 目录属主不是登录用户 | chown 到对应用户 |
| 某天突然连不上 | 本地 VSCode 自动升级了版本 | 按新 commit-id 重新部署并归档旧包 |
6. 批量部署与团队维护心得
6.1 把部署脚本固化下来
手敲命令容易出错,整理成一个脚本后,批量部署只是执行一遍的事。下面这个脚本是我日常用的简化版:
#!/usr/bin/env bash set -euo pipefail TARGET="$1" # user@host COMMIT_ID="e180c05f7f13" ARCH="x64" TARBALL="/tmp/vscode-server-linux-${ARCH}.tar.gz" ssh "$TARGET" "mkdir -p ~/.vscode-server/bin/${COMMIT_ID}" scp "$TARBALL" "$TARGET:/tmp/" ssh "$TARGET" "tar -xzf /tmp/vscode-server-linux-${ARCH}.tar.gz \ -C ~/.vscode-server/bin/${COMMIT_ID} --strip-components=1 && \ ls ~/.vscode-server/bin/${COMMIT_ID}/server.sh"脚本里把COMMIT_ID、ARCH、TARBALL显式定义出来,后面换版本时只改这三行。如果服务器 SSH 端口不是 22,记得在ssh和scp命令里加上-p参数。
6.2 建立版本缓存库
我建议在团队内部维护一个共享归档目录,按 VSCode 版本号分类:
vscode-server-offline/ 1.86.2/ server-linux-x64.tar.gz server-linux-arm64.tar.gz python.vsix go.vsix每次有人升级本地 VSCode,就在归档目录里补一个新 server 包。以后给新机器配环境,直接从共享目录拷贝,不用再让每个人去外网找下载链接。这个方法特别适合多台离线机器的批量交付。
6.3 让团队少踩坑的关键
踩过几次坑之后,我最深的体会是:离线 Remote-SSH 的本质不是网络问题,而是版本管理问题。只要 commit-id 对上、目录结构放对,server 包搬过去就能跑。所以我给团队定了一条规则:本地 VSCode 大版本升级前先通知管理归档的人,server 包同步更新后再升级。这样做的效果很明显,自从统一管理之后,离线机器上再也没出现过“连不上还得现场救火”的情况。
如果你正准备给一批离网机器配环境,先用一台机器把流程完整跑一遍,再决定是手动操作还是写脚本。等脚本和归档目录固定下来,后面的每一台新机器都会变得非常省事。