从 0 到 1:用 GitHub 学生包白嫖服务器和域名,部署一个自己的 sub2api 中转站


这篇文章记录的是我从零开始搭一个 sub2api 自建中转站的完整过程。 目标很明确:

  1. 白嫖一台云服务器和一个域名。
  2. 用 VS Code 远程连接服务器,尽量减少纯命令行操作的痛苦。
  3. 用 Docker 部署 sub2api
  4. 用域名和 HTTPS 对外提供访问。
  5. 顺手把几个最容易卡住的坑一次性讲清楚。

如果你和我一样,之前对 Linux、Nginx、SSL 证书都不算熟,这篇文章应该能帮你少走很多弯路。

一、准备工作

这次用到的核心资源有两个:

  • 一台 Ubuntu 云服务器
  • 一个自己的域名

这两个资源我这次主要是通过 GitHub Education Pack 官方入口 领取到的。

我这次用的是 GitHub Education Pack 里提供的优惠和额度。 学生认证通过之后,通常能拿到一些云厂商和域名服务商的试用权益,足够把这一套先跑起来。

下图中间是服务器相关权益,右边是域名相关权益:

Pasted image 20260404185151

如果你只是想先把整套链路跑通,不追求长期稳定商用,那么学生包确实是一个很划算的起点。

Name.com 的学生权益入口 也可以直接从这里进。

Pasted image 20260404203144

服务器购买这一步我参考的是 SkYFly2233/NEU-ipv6-proxy 里的流程,至少先把服务器买好并确认能 ping 通。

二、先把域名和服务器准备好

当你手里已经有了一个域名和一台服务器,第一件事不是立刻装程序,而是先把域名解析链路打通。

2.1 修改域名的 Nameserver

域名需要先把 Nameserver 指向你的云服务商,这样后续就能直接在云服务商的控制台里配置 DNS 记录。

我这里的做法是把域名托管到服务器提供商对应的 DNS 平台,方便后续统一管理。

操作过程大概如下:

Pasted image 20260404185913

Pasted image 20260404190043

Pasted image 20260404190328

不同平台的界面不一样,但思路是一样的: 找到域名服务商后台,把原有的 Nameserver 替换为云服务商给你的那几条记录。

以 DigitalOcean 为例,默认一般是这三条:

ns1.digitalocean.com
ns2.digitalocean.com
ns3.digitalocean.com

如果你不确定具体应该填什么,可以直接去你购买服务器的平台文档里查:

Pasted image 20260404191052

2.2 配置 A 记录

Nameserver 切过去之后,就可以在云服务商的 DNS 面板里配置域名解析了。

最关键的是两条:

  • @ 指向你的服务器公网 IP
  • www 指向同一台服务器公网 IP

配置示意如下:

Pasted image 20260404191849

Pasted image 20260404191814

配置完成后,不要急着继续。 先确认解析有没有真正生效。

Windows PowerShell 可以直接这样测:

Resolve-DnsName liutan.codes -Type A -Server 8.8.8.8 | Format-List *

如果正常,结果里应该能看到你服务器的公网 IP。比如:

Address      : 64.23.155.30
IPAddress    : 64.23.155.30
QueryType    : A
IP4Address   : 64.23.155.30
Name         : liutan.codes
Type         : A
TTL          : 3600

这一步成功后,说明你的域名已经正确指向服务器了。

三、为什么我强烈建议用 VS Code 远程连接服务器

很多服务器厂商后台都会提供在线终端,紧急情况下确实能救命:

Pasted image 20260404203656

Pasted image 20260404203817

后面部署 sub2api 的过程,本质上就是一堆 Linux 运维操作。 如果你一直用网页终端硬敲,体验其实比较差,尤其是:

  • 容易复制错命令
  • heredoc 和多行命令非常容易翻车
  • 查日志、改配置文件都不方便

所以我最后的选择是:

  • 本地用 Windows
  • 编辑器用 VS Code
  • 通过 Remote-SSH 远程连到服务器

这样体验会好很多,基本可以把云服务器当成本地文件夹来操作。

3.1 生成 SSH 密钥

本地 PowerShell 执行:

ssh-keygen -t ed25519 -C "your_name@windows"

执行后会依次提示你输入几个东西:

  • Enter file in which to save the key:直接回车,使用默认路径即可。
  • Enter passphrase:可选。如果只是自己用、图省事,也可以留空直接回车。
  • Enter same passphrase again:如果前面设置了口令,这里再输一遍。

完成后,会生成两份文件:

  • 私钥:C:\Users\你的用户名\.ssh\id_ed25519
  • 公钥:C:\Users\你的用户名\.ssh\id_ed25519.pub

查看公钥:

Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub

3.2 把公钥放到服务器

这一步建议先在服务器的网页终端里完成,因为此时你本地 SSH 还不一定已经打通。

先在服务器上准备好 .ssh 目录和授权文件:

install -d -m 700 /root/.ssh
touch /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
chown -R root:root /root/.ssh

然后打开这个文件:

nano /root/.ssh/authorized_keys

把刚才在本地 Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub 输出的整行公钥完整粘贴进去。 注意这里一定要满足两个条件:

  • 公钥必须完整,不要截断。
  • 公钥必须单独占一整行,前后不要和别的内容粘在一起。

保存退出后,可以再执行一次:

chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys
grep -n 'your_name@windows' /root/.ssh/authorized_keys

如果 grep 能搜到你刚才那行公钥,说明这一步基本没问题了。

这一步如果出错,我自己踩到的典型原因有:

  • 公钥没有单独占一整行
  • authorized_keys 被错误覆盖成空文件
  • 权限不对

如果你想先不写别名,直接测试原始连接,也可以在本地 PowerShell 里这样连:

ssh -i $env:USERPROFILE\.ssh\id_ed25519 -p 2222 root@64.23.155.30

如果你的服务器还是默认 SSH 端口,就把 -p 2222 改成 -p 22,或者干脆删掉这段端口参数。

3.3 配置本地 SSH 别名

我最后在本地的 C:\Users\ly\.ssh\config 里用了这样的配置:

Host do-server
  HostName 64.23.155.30
  User root
  Port 2222
  IdentityFile C:\Users\ly\.ssh\id_ed25519

这里的 2222 是我后面实际使用的 SSH 端口。 如果你没有改过端口,那一般还是默认的 22。 如果你前面测试直连时已经确认端口没问题,那这里一定要和实际端口保持一致。

配置完成后,直接测试:

ssh do-server

如果能正常进入服务器,说明 SSH 这一关就算打通了。

3.4 VS Code 远程连接

安装 Remote - SSH 插件之后,在 VS Code 里连接这个 do-server。 连上之后,左下角会显示:

SSH: do-server

Pasted image 20260404203407

这时候你打开的就不是本地目录,而是服务器上的远程目录了。 后面的 Docker 配置、Nginx 配置、查看日志,都可以在这个窗口里完成。

这里建议顺手再做两个确认:

  1. VS Code 左下角确实显示 SSH: do-server
  2. 远程终端里执行 pwd 时,看到的是 Linux 路径,比如 /root/opt/sub2api-deploy

只要这两个都对了,说明你现在操作的就是真正的远程服务器,不是本地窗口。

做到这一步之后,后面很多调试工作其实都可以直接交给 AI agent 来辅助完成了。 比如 CodexClaude Code 之类的工具,都可以通过你本地已经配置好的 SSH 环境,直接在远程服务器上读文件、改配置、看日志和执行命令。

但这一点一定要谨慎:如果使用不当,AI agent 同样可能对云服务器环境造成不可逆的破坏。 尤其是涉及删除、覆盖、重置、批量改配置这类操作时,最好先确认命令再执行。

codex中使用

Pasted image 20260405092935

给他一个task

Pasted image 20260405094837

Pasted image 20260405094931

四、在 Ubuntu 服务器上安装 Docker

sub2api 官方已经提供了 Docker 部署方式,所以不需要手动装 Go 环境,直接走容器会省心很多。

先更新系统:

apt update
apt install -y ca-certificates curl

然后按 Docker 官方仓库方式安装:

install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

写入 Docker 源:

cat >/etc/apt/sources.list.d/docker.sources <<'EOF'
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: noble
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF

安装 Docker:

apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

检查是否安装成功:

docker --version
docker compose version
systemctl status docker --no-pager

五、部署 sub2api

接下来正式开始部署 sub2api

项目地址:Wei-Shaw/sub2api

我这次把部署目录放在:

/opt/sub2api-deploy

这样目录干净,也符合 Linux 里“服务类程序放在 /opt”的习惯。

5.1 初始化部署目录

mkdir -p /opt/sub2api-deploy
cd /opt/sub2api-deploy
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh | bash

这个脚本会自动生成:

  • docker-compose.yml
  • .env
  • .env.example
  • data/
  • postgres_data/
  • redis_data/

同时还会帮你生成一些安全密钥,比如:

  • POSTGRES_PASSWORD
  • JWT_SECRET
  • TOTP_ENCRYPTION_KEY

5.2 启动容器

cd /opt/sub2api-deploy
docker compose up -d

查看状态:

docker compose ps
docker ps

如果一切正常,你会看到三个核心容器:

  • sub2api
  • sub2api-postgres
  • sub2api-redis

并且状态一般会显示为:

Up (healthy)

5.3 初始访问

如果你还没做反向代理,可以直接先访问:

http://服务器公网IP:8080

能打开说明 sub2api 本体已经跑起来了。

如果你第一次启动后忘了管理员密码,也可以直接在服务器上查启动日志:

docker logs sub2api 2>&1 | grep -i "admin password"

正常会看到类似输出:

Generated admin password (one-time): 6d60f341ba924a7a6042c8621b9d0d75

这就是首次自动生成的管理员密码。 登录后台之后,这个密码也可以在面板设置里再修改成你自己更容易记住的版本。

六、给 sub2api 绑定域名

直接用 :8080 虽然能用,但不太像一个正经服务。 更常见的做法是:

  • 外部访问 https://你的域名
  • Nginx 转发到本机的 127.0.0.1:8080

这样你最终只对外暴露 80 和 443 端口。

6.1 安装 Nginx

apt install -y nginx
systemctl enable --now nginx

6.2 配置反向代理

我最终使用的思路是:

  • 域名入口:https://liutan.codes
  • 后端服务:http://127.0.0.1:8080

一个最小可用的 Nginx 配置大概是这样:

server {
    listen 80;
    server_name liutan.codes www.liutan.codes;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        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;
    }
}

写完之后检查并重载:

nginx -t
systemctl reload nginx

这时访问:

http://liutan.codes

理论上就应该能看到 sub2api 页面了。

七、配置 HTTPS 证书

证书这一步我一开始也觉得复杂,后来发现真正走下来并不难。 最省事的组合就是:

  • Nginx
  • Certbot
  • Let's Encrypt

7.1 安装 Certbot

apt install -y certbot python3-certbot-nginx

7.2 申请证书

certbot --nginx -d liutan.codes -d www.liutan.codes

按提示输入邮箱、同意协议之后,它会自动:

  • 申请证书
  • 改写 Nginx 配置
  • 配置 80 跳转到 443

成功后会看到类似输出:

Successfully received certificate.
Congratulations! You have successfully enabled HTTPS on https://liutan.codes and https://www.liutan.codes

证书是免费的。 使用的是 Let's Encrypt,默认有效期 90 天,但 Certbot 会自动续期。

检查自动续期:

systemctl status certbot.timer

只要它是 active (waiting),一般就不用太操心。

八、我踩过的几个坑

这一部分我觉得比教程本身还重要,因为真正折腾人的往往不是“标准步骤”,而是那些看起来很小但会把你卡半天的问题。

8.1 VS Code Remote-SSH 一直连接失败

我一开始遇到的情况是:

  • 本地 ssh 命令连不上
  • VS Code Remote-SSH 一直报错
  • 远程连接日志里不断出现 Connection closed

后来排查下来,核心问题主要有三个:

  1. authorized_keys 内容写坏了,公钥没有独立成行。
  2. SSH 端口被我改过,VS Code 还在按旧端口连接。
  3. 自己反复点了 “Uninstall VS Code Server”,把远程组件清掉了。

真正稳定下来之后,反而很简单:

  • 本地 ssh do-server 先能成功
  • ~/.ssh/config 写正确
  • VS Code 连接同一个 Host 别名

只要命令行 SSH 能通,VS Code 大概率也能通。

8.2 SMTP 发信一直失败

sub2api 的邮箱配置我也踩了一个大坑。 刚开始以为是 Gmail 或 QQ 邮箱配置错了,后来才发现不是账号问题,而是云服务器网络出口的问题。

我这台 DigitalOcean Droplet 上的情况是:

  • smtp.gmail.com:465 超时
  • smtp.gmail.com:587 超时
  • smtp.qq.com:465/587 同样不稳定

2525 这类替代端口能通。

这意味着: 很多云厂商会默认限制传统 SMTP 端口,防止新号被拿去发垃圾邮件。

如果你也遇到类似问题,建议直接考虑这两条路:

  1. 用支持 2525 端口的服务商,例如 SendGrid、Mailgun。
  2. 如果只是自用测试,先关闭邮箱验证相关功能。

8.3 本地开了 TUN,到底走的是谁的流量

这也是我后来专门确认过的问题。

如果你本地浏览器访问的是:

https://www.liutan.codes

那么这段流量是否走代理,取决于你本地代理软件的规则。 如果规则命中了代理节点,比如日志里出现:

[TCP] 127.0.0.1:50099 --> www.liutan.codes:443 match using 某节点

那说明:

  • 你本地到自己服务器域名这一段,走的是你本地代理流量
  • 会消耗你本地代理套餐

但服务器上的 sub2api 自己再去访问上游接口时,走的是服务器网络,不是你本地 TUN。

所以这其实是两段流量:

  1. 你本地访问自己域名:可能走本地代理
  2. 服务器访问外部服务:走服务器出口

如果你不想让访问自己服务器也消耗代理流量,最简单的办法是把自己的域名加到 DIRECT 规则里。

九、部署完成后,怎么判断服务是不是正常常驻

sub2api 是跑在 Docker 里的,所以后面排查状态时,我最常用的是这几条命令:

查看容器状态:

docker ps

查看项目状态:

cd /opt/sub2api-deploy
docker compose ps

看日志:

cd /opt/sub2api-deploy
docker compose logs -f sub2api

重启:

cd /opt/sub2api-deploy
docker compose restart sub2api

只要容器状态显示:

Up (healthy)

基本就说明服务是正常常驻的。

十、我最后得到的是什么

做到这里,你最终会得到一套完整的可用方案:

  • 一台自己的 Ubuntu 云服务器
  • 一个已经正确解析的域名
  • 一套能稳定连接的 SSH / VS Code 远程开发环境
  • 一个通过 Docker 跑起来的 sub2api
  • 一个通过 Nginx + Certbot 暴露出来的 HTTPS 入口

也就是说,你后续访问时,不再需要记:

http://ip:8080

而是直接:

https://你的域名

体验和维护成本都会好很多。

十一、写在最后

这次折腾下来,我最大的感受是:

自建服务真正难的,往往不是把程序跑起来,而是把整条链路打通:

  • 域名解析
  • SSH 登录
  • VS Code 远程连接
  • Docker 部署
  • 反向代理
  • HTTPS
  • 邮件和网络限制

只要这几件事理顺了,后面继续在同一台服务器上加别的服务,其实就会轻松很多。 比如我后面又把别的 Python 小工具挂到了同一个域名的子路径下,本质上还是同样的思路。

如果你只是想先跑通一版,建议顺序一定按下面来:

  1. 先确认域名解析正常。
  2. 再确认 SSH 能稳定连上。
  3. 然后再装 Docker 和部署 sub2api
  4. 最后再折腾 Nginx 和 HTTPS。

不要一上来就一股脑全部同时改,不然一旦出错,很难判断到底是哪一层出了问题。

如果后面我把这套流程继续整理得更系统一点,我会再单独写:

  • sub2api 的配置项说明
  • SMTP 发信的替代方案
  • 如何在同一台服务器上挂多个 Web 工具
  • 如何把运维过程尽量迁移到 VS Code 里完成

至少到这里,这台“自建中转站”已经算是一个能长期用下去的版本了。


  目录
布布和一二