结论先行:一台 VPS 想稳定跑几十个项目,抓住一条主线:对外只开 80/443 两个口,全交给 nginx 按域名分流;内部所有服务一律绑 127.0.0.1,端口分段占位(8000-8999 生产、9000-9999 临时,数据库如 3306/5432/6379 只听本地)。再维护一张 PORTS.md 端口清单,新项目照表分配、不踩别人的坑。这套组合下,4 核 8G 跑 20-30 个中小项目没问题,50 个则要看每个的实际负载。

你买了台 4 核 8G 的 VPS,打算上面跑 10 个项目。结果跑到第 5 个就乱套了:端口冲突、配置散落各处、改一个服务影响另一个。这篇文章教你如何从第一天就规划好。

全景图解导览:一台VPS管理多个项目不乱套:端口规划指南 STAGE 01 · 问题起爆 期望目标 • ✅端口资源规划:合理分配 65535个端口,避免冲突 • ✅统一入口管理:用nginx… 定位核心风险卡点 STAGE 02 · 机制拆解 计划思考 • 8000-8999:长期运行的应 用服务 • 9000-9999:临时、测试项… 理清底层因果逻辑 STAGE 03 · 方案对照 操作步骤 • 扫描现有端口占用:开始规 划前,先摸清现状: • 创建端口分配表:创建一个… 对齐量化实测标尺 STAGE 04 · 工程落地 系统服务 • 22:SSH→0.0.0.0→已改 22000 • 80:HTTP→0.0.0.0→… 锁死生产确定性

1、期望目标

这篇文章帮你搞定:

  • ✅ 端口资源规划:合理分配 65535 个端口,避免冲突
  • ✅ 统一入口管理:用 nginx 反向代理,只对外暴露 80/443
  • ✅ 标准部署流程:新项目 5 分钟上线,不破坏现有服务
  • ✅ 端口清单追踪:清楚知道哪个端口被谁占用
  • ✅ 安全加固:敏感端口仅监听 localhost,防火墙精确控制

做到这些,你的 VPS 可以轻松承载 10+ 甚至更多项目,而且井井有条。

问题现场解剖:期望目标 ⚠️ 从问题出发:表面症状是如何一步步演变为生产故障或降权的? 独立开发者工具箱 · CH-56 · 核心卡点传导链 1. ✅端口资源规划 合理分配65535个端口,避免冲 突 症结:认知偏差与配置盲区 2. ✅统一入口管理 用nginx反向代理,只对外暴露 80/443 传导:隐性劣化与边界失守 3. ✅标准部署流程 新项目5分钟上线,不破坏现有 服务 破局:量化指标与工程验证 ✅ 破局核心原则(结论先行) 一台VPS想稳定跑几十个项目,抓住一条主线:对外只开80/443两个口,全交给 nginx按域名分流;内部所有服务一律绑127.0.0.1,端口分段占位(8000-8999…

2、计划思考

端口资源全貌

Linux 系统的端口分为三段,每段有不同用途:

端口资源分配全景图 系统端口 0 - 1023 注册端口 1024 - 49151 动态端口 49152 - 65535 需要 root 权限 应用服务分配区域 临时连接使用 我们的分配策略 8000-8999 应用服务 9000-9999 临时/测试服务 127.0.0.1 数据库仅本地

系统端口(0-1023):需要 root 权限绑定,避开不碰。80/443 给 nginx,22 给 SSH。

注册端口(1024-49151):这是我们的主战场。推荐分配:

  • 8000-8999:长期运行的应用服务
  • 9000-9999:临时、测试项目
  • 3306、5432、6379 等:数据库仅监听 127.0.0.1

动态端口(49152-65535):系统自动分配,不主动使用。

📌 为什么长期服务压在 8000-8999:IANA 规定的动态端口从 49152 起,但 Linux 给出站连接自动分配的临时端口(ephemeral range)默认是 32768-60999,用 sysctl net.ipv4.ip_local_port_range 能查到。把长期服务放在这个区间下方(8000-8999),就不会跟系统临时出站端口撞车。这才是分段划分背后的工程理由,不是拍脑袋定的。

三种部署方案对比

方案 优点 缺点 适用场景
直接暴露端口 配置简单,每个域名:端口访问 端口多记不住、不安全、SSL 证书麻烦 临时测试
nginx 反向代理 统一入口、安全、易于管理 需要 nginx 配置 生产环境推荐
Docker + 网络 隔离好、可移植 复杂度高,资源开销大 微服务架构

推荐方案:nginx 反向代理。对外只开 80/443,内部服务用不同端口,通过域名区分。

端口分配原则

  1. 按段分配,便于记忆

    • 8000-8999:生产应用
    • 9000-9999:测试/临时
    • 10 进制递增,留空隙
  2. 数据库仅监听 localhost

    • MySQL 3306、PostgreSQL 5432、Redis 6379、MongoDB 27017
    • 不对外暴露,通过应用访问
  3. 文档化追踪

    • 维护一个端口清单文件
    • 每个新项目先查表再分配
  4. 标准化流程

    • 新项目部署按固定步骤
    • 减少人为失误

3、操作步骤

3.1 扫描现有端口占用

开始规划前,先摸清现状:

1
2
3
4
5
6
# 查看所有监听中的端口
ss -tulnp | grep LISTEN

# 或者用 netstat(如果没有 net-tools,先安装)
sudo apt install net-tools
netstat -tulnp | grep LISTEN

输出类似:

1
2
3
4
tcp   LISTEN 0   128    *:80     *:*   users:(("nginx",pid=1234,fd=6))
tcp LISTEN 0 128 *:443 *:* users:(("nginx",pid=1234,fd=7))
tcp LISTEN 0 128 *:22 *:* users:(("sshd",pid=567,fd=3))
tcp LISTEN 0 70 127.0.0.1:3306 0.0.0.0:* users:(("mysqld",pid=890,fd=15))

💡 参数解释:

  • XX:TCP 协议
  • XX:UDP 协议
  • XX:监听状态
  • XX:显示端口号而非服务名
  • XX:显示进程信息

记录当前占用情况,建议保存为文件:

1
2
# 记录到文件
ss -tulnp | grep LISTEN > ~/port-list-$(date +%Y%m%d).txt

3.2 创建端口分配表

创建一个端口清单文件,方便后续查询:

1
2
# 创建端口清单文件
vim ~/port-registry.md

内容模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# 端口分配清单

更新时间:2026-08-07

<svg width="100%" viewBox="0 0 860 250" xmlns="http://www.w3.org/2000/svg">
<rect width="860" height="250" rx="12" fill="#0f172a"/>
<text x="430" y="34" fill="#f8fafc" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">落地执行与验证流水线:操作步骤</text>
<text x="430" y="55" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle">按序执行以下四步流程,把定性经验转化为可自动巡检的工程防线</text>
<rect x="24" y="76" width="186" height="148" rx="10" fill="#1e293b" stroke="#38bdf8" stroke-width="1.8"/>
<rect x="38" y="90" width="76" height="24" rx="12" fill="#0f172a"/>
<text x="76" y="106" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">STEP 01</text>
<text x="117" y="138" fill="#f8fafc" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">扫描现有端口占用</text>
<text x="117" y="168" fill="#cbd5e1" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle">开始规划前,先摸清现</text>
<text x="117" y="188" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">状:</text>
<line x1="210" y1="150" x2="230" y2="150" stroke="#38bdf8" stroke-width="2"/>
<rect x="230" y="76" width="186" height="148" rx="10" fill="#1e293b" stroke="#38bdf8" stroke-width="1.8"/>
<rect x="244" y="90" width="76" height="24" rx="12" fill="#0f172a"/>
<text x="282" y="106" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">STEP 02</text>
<text x="323" y="138" fill="#f8fafc" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">创建端口分配表</text>
<text x="323" y="168" fill="#cbd5e1" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle">创建一个端口清单文件,</text>
<text x="323" y="188" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">方便后续查询:</text>
<line x1="416" y1="150" x2="436" y2="150" stroke="#38bdf8" stroke-width="2"/>
<rect x="436" y="76" width="186" height="148" rx="10" fill="#1e293b" stroke="#38bdf8" stroke-width="1.8"/>
<rect x="450" y="90" width="76" height="24" rx="12" fill="#0f172a"/>
<text x="488" y="106" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">STEP 03</text>
<text x="529" y="138" fill="#f8fafc" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">标准化加固与重构</text>
<text x="529" y="168" fill="#cbd5e1" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle">注入经过验证的脚本与架</text>
<text x="529" y="188" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">构模板</text>
<line x1="622" y1="150" x2="642" y2="150" stroke="#38bdf8" stroke-width="2"/>
<rect x="642" y="76" width="186" height="148" rx="10" fill="#064e3b" stroke="#10b981" stroke-width="1.8"/>
<rect x="656" y="90" width="76" height="24" rx="12" fill="#0f172a"/>
<text x="694" y="106" fill="#10b981" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">STEP 04</text>
<text x="735" y="138" fill="#f8fafc" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">持续监控与回归审计</text>
<text x="735" y="168" fill="#cbd5e1" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle">接入定时巡检防止问题二</text>
<text x="735" y="188" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">次复发</text>
</svg>

## 系统服务
| 端口 | 服务 | 监听地址 | 备注 |
|------|------|---------|------|
| 22 | SSH | 0.0.0.0 | 已改 22000 |
| 80 | HTTP | 0.0.0.0 | nginx |
| 443 | HTTPS | 0.0.0.0 | nginx |

<svg width="100%" viewBox="0 0 860 340" xmlns="http://www.w3.org/2000/svg">
<rect width="860" height="340" rx="12" fill="#0f172a"/>
<text x="430" y="34" fill="#f8fafc" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">多维实测与判断标尺矩阵:系统服务</text>
<rect x="30" y="56" width="180" height="38" rx="6" fill="#1e293b" stroke="#38bdf8" stroke-width="1.5"/>
<text x="120" y="80" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">端口</text>
<rect x="220" y="56" width="280" height="38" rx="6" fill="#1e293b" stroke="#a855f7" stroke-width="1.5"/>
<text x="360" y="80" fill="#cbd5e1" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">服务</text>
<rect x="510" y="56" width="320" height="38" rx="6" fill="#064e3b" stroke="#10b981" stroke-width="1.5"/>
<text x="670" y="80" fill="#a7f3d0" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">监听地址</text>
<rect x="30" y="108" width="800" height="44" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<rect x="30" y="108" width="180" height="44" rx="6" fill="#0f172a"/>
<text x="120" y="135" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">22</text>
<text x="360" y="135" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle">SSH</text>
<line x1="510" y1="114" x2="510" y2="146" stroke="#334155" stroke-width="1"/>
<text x="670" y="135" fill="#6ee7b7" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">0.0.0</text>
<rect x="30" y="162" width="800" height="44" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<rect x="30" y="162" width="180" height="44" rx="6" fill="#0f172a"/>
<text x="120" y="189" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">80</text>
<text x="360" y="189" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle">HTTP</text>
<line x1="510" y1="168" x2="510" y2="200" stroke="#334155" stroke-width="1"/>
<text x="670" y="189" fill="#6ee7b7" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">0.0.0</text>
<rect x="30" y="216" width="800" height="44" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<rect x="30" y="216" width="180" height="44" rx="6" fill="#0f172a"/>
<text x="120" y="243" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">443</text>
<text x="360" y="243" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle">HTTPS</text>
<line x1="510" y1="222" x2="510" y2="254" stroke="#334155" stroke-width="1"/>
<text x="670" y="243" fill="#6ee7b7" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">0.0.0</text>
</svg>

## 数据库(仅本地)
| 端口 | 服务 | 监听地址 | 备注 |
|------|------|---------|------|
| 3306 | MySQL | 127.0.0.1 | |
| 5432 | PostgreSQL | 127.0.0.1 | |
| 6379 | Redis | 127.0.0.1 | |
| 27017 | MongoDB | 127.0.0.1 | |

<svg width="100%" viewBox="0 0 860 340" xmlns="http://www.w3.org/2000/svg">
<rect width="860" height="340" rx="12" fill="#0f172a"/>
<text x="430" y="34" fill="#f8fafc" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">多维实测与判断标尺矩阵:数据库(仅本地)</text>
<rect x="30" y="56" width="180" height="38" rx="6" fill="#1e293b" stroke="#38bdf8" stroke-width="1.5"/>
<text x="120" y="80" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">端口</text>
<rect x="220" y="56" width="280" height="38" rx="6" fill="#1e293b" stroke="#a855f7" stroke-width="1.5"/>
<text x="360" y="80" fill="#cbd5e1" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">服务</text>
<rect x="510" y="56" width="320" height="38" rx="6" fill="#064e3b" stroke="#10b981" stroke-width="1.5"/>
<text x="670" y="80" fill="#a7f3d0" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">监听地址</text>
<rect x="30" y="108" width="800" height="44" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<rect x="30" y="108" width="180" height="44" rx="6" fill="#0f172a"/>
<text x="120" y="135" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">3306</text>
<text x="360" y="135" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle">MySQL</text>
<line x1="510" y1="114" x2="510" y2="146" stroke="#334155" stroke-width="1"/>
<text x="670" y="135" fill="#6ee7b7" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">0.0.1</text>
<rect x="30" y="162" width="800" height="44" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<rect x="30" y="162" width="180" height="44" rx="6" fill="#0f172a"/>
<text x="120" y="189" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">5432</text>
<text x="360" y="189" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle">PostgreSQL</text>
<line x1="510" y1="168" x2="510" y2="200" stroke="#334155" stroke-width="1"/>
<text x="670" y="189" fill="#6ee7b7" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">0.0.1</text>
<rect x="30" y="216" width="800" height="44" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<rect x="30" y="216" width="180" height="44" rx="6" fill="#0f172a"/>
<text x="120" y="243" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">6379</text>
<text x="360" y="243" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle">Redis</text>
<line x1="510" y1="222" x2="510" y2="254" stroke="#334155" stroke-width="1"/>
<text x="670" y="243" fill="#6ee7b7" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">0.0.1</text>
<rect x="30" y="270" width="800" height="44" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<rect x="30" y="270" width="180" height="44" rx="6" fill="#0f172a"/>
<text x="120" y="297" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">27017</text>
<text x="360" y="297" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle">MongoDB</text>
<line x1="510" y1="276" x2="510" y2="308" stroke="#334155" stroke-width="1"/>
<text x="670" y="297" fill="#6ee7b7" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">0.0.1</text>
</svg>

## 应用服务(8000-8999)
| 端口 | 项目 | 域名 | 路径 | 状态 |
|------|------|------|------|------|
| 8001 | blog-api | api.example.com | /var/www/blog-api | ✅ |
| 8002 | ssa-api | api.ssa.dev | /var/www/ssa-api | ✅ |
| 8003 | - | - | - | 空闲 |
| 8004 | - | - | - | 空闲 |

<svg width="100%" viewBox="0 0 860 340" xmlns="http://www.w3.org/2000/svg">
<rect width="860" height="340" rx="12" fill="#0f172a"/>
<text x="430" y="34" fill="#f8fafc" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">多维实测与判断标尺矩阵:应用服务(8000-8999)</text>
<rect x="30" y="56" width="180" height="38" rx="6" fill="#1e293b" stroke="#38bdf8" stroke-width="1.5"/>
<text x="120" y="80" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">端口</text>
<rect x="220" y="56" width="280" height="38" rx="6" fill="#1e293b" stroke="#a855f7" stroke-width="1.5"/>
<text x="360" y="80" fill="#cbd5e1" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">项目</text>
<rect x="510" y="56" width="320" height="38" rx="6" fill="#064e3b" stroke="#10b981" stroke-width="1.5"/>
<text x="670" y="80" fill="#a7f3d0" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">域名</text>
<rect x="30" y="108" width="800" height="44" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<rect x="30" y="108" width="180" height="44" rx="6" fill="#0f172a"/>
<text x="120" y="135" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">8001</text>
<text x="360" y="135" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle">blog-api</text>
<line x1="510" y1="114" x2="510" y2="146" stroke="#334155" stroke-width="1"/>
<text x="670" y="135" fill="#6ee7b7" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">api.example.com</text>
<rect x="30" y="162" width="800" height="44" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<rect x="30" y="162" width="180" height="44" rx="6" fill="#0f172a"/>
<text x="120" y="189" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">8002</text>
<text x="360" y="189" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle">ssa-api</text>
<line x1="510" y1="168" x2="510" y2="200" stroke="#334155" stroke-width="1"/>
<text x="670" y="189" fill="#6ee7b7" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">api.ssa.dev</text>
<rect x="30" y="216" width="800" height="44" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<rect x="30" y="216" width="180" height="44" rx="6" fill="#0f172a"/>
<text x="120" y="243" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">8003</text>
<text x="360" y="243" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle"></text>
<line x1="510" y1="222" x2="510" y2="254" stroke="#334155" stroke-width="1"/>
<text x="670" y="243" fill="#6ee7b7" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle"></text>
<rect x="30" y="270" width="800" height="44" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<rect x="30" y="270" width="180" height="44" rx="6" fill="#0f172a"/>
<text x="120" y="297" fill="#38bdf8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">8004</text>
<text x="360" y="297" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="11" text-anchor="middle"></text>
<line x1="510" y1="276" x2="510" y2="308" stroke="#334155" stroke-width="1"/>
<text x="670" y="297" fill="#6ee7b7" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle"></text>
</svg>

## 临时服务(9000-9999)
| 端口 | 项目 | 用途 | 截止日期 |
|------|------|------|---------|
| 9001 | test-v1 | 功能测试 | 2026-07-10 |
| 9002 | - | - | 空闲 |

💡 小技巧:把这个文件放到 git 仓库,改完提交。团队协作时避免端口冲突。

3.3 配置 nginx 反向代理

假设你有一个 Node.js 应用运行在 8001 端口,想通过 api.example.com 访问。

步骤一:检查并安装 nginx

1
2
3
4
# 检查 nginx 是否安装
nginx -v
# 如果没有,先安装
sudo apt update && sudo apt install nginx -y

步骤二:创建 nginx 配置

1
2
# 在 /etc/nginx/sites-available/ 创建配置
sudo vim /etc/nginx/sites-available/api.example.com

步骤三:配置内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# api.example.com
server {
listen 80;
server_name api.example.com;

# 日志
access_log /var/log/nginx/api.example.com.access.log;
error_log /var/log/nginx/api.example.com.error.log;

# 反向代理到后端应用
location / {
proxy_pass http://127.0.0.1:8001;
proxy_http_version 1.1;

# 传递真实 IP
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;

# WebSocket 支持(如果需要)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}

步骤四:启用配置

1
2
3
4
5
6
7
8
# 创建软链接到 sites-enabled
sudo ln -s /etc/nginx/sites-available/api.example.com /etc/nginx/sites-enabled/

# 测试配置语法
sudo nginx -t

# 重载 nginx
sudo systemctl reload nginx

步骤五:配置 SSL 证书(推荐用 Let’s Encrypt)

1
2
3
4
5
6
7
# 安装 certbot
sudo apt install certbot python3-certbot-nginx

# 自动申请证书并配置 nginx
sudo certbot --nginx -d api.example.com

# certbot 会自动修改配置,添加 443 监听和证书路径

完成后访问 https://api.example.com,流量会自动转发到内网的 8001 端口。

nginx 反向代理流量流向 用户 443 互联网 :443 nginx 反向代理 SSL 终结 :8001 应用 127.0.0.1 外部只能访问 80/443,内部端口不暴露 应用仅监听 localhost,安全隔离

3.4 新项目标准部署流程

假设新项目 new-project,域名 new.example.com:

第一步:分配端口

1
2
3
4
# 查看端口清单
cat ~/port-registry.md | grep 800

# 找到空闲端口,假设 8005 空闲

第二步:创建应用用户(可选,隔离更好)

1
2
3
4
5
# 创建专用用户(不需要 sudo 权限)
sudo adduser newproject-app

# 切换到新用户
su - newproject-app

第三步:部署应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 克隆代码
git clone https://github.com/yourname/new-project.git ~/new-project
cd ~/new-project

# 安装依赖(以 Node.js 为例)
npm install

# 创建 .env 配置
cat > .env <<EOF
PORT=8005
NODE_ENV=production
DB_HOST=127.0.0.1
DB_PORT=3306
EOF

# 启动应用(根据你的项目调整命令)
npm run start
# 或 node app.js
# 或 pm2 start app.js

⚠️ 重要:应用必须监听 127.0.0.1:8005,不要监听 0.0.0.0:8005,否则会对外暴露。

第零步:配置 DNS

在域名注册商处添加 A 记录指向 VPS IP:

1
2
3
4
# 类型:A
# 名称:new
# 值:你的VPS IP
# TTL:600

等待生效后验证:

1
ping new.example.com

第四步:配置 nginx

1
2
3
4
5
# 回到 root
exit

# 创建 nginx 配置
sudo vim /etc/nginx/sites-available/new.example.com

内容:

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
server_name new.example.com;

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

启用配置:

1
2
3
sudo ln -s /etc/nginx/sites-available/new.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

第五步:申请 SSL

1
sudo certbot --nginx -d new.example.com

第六步:更新端口清单

1
vim ~/port-registry.md

添加:

1
| 8005 | new-project | new.example.com | /home/newproject-app/new-project | ✅ |

第七步:配置进程守护(用 PM2 或 systemd)

方法 A:PM2(Node.js 推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
# 切换回应用用户
su - newproject-app

# 安装 PM2
npm install -g pm2

# 启动应用
cd ~/new-project
pm2 start npm --name "new-project" -- start

# 保存 PM2 配置
pm2 save
pm2 startup

方法 B:systemd(通用)

1
2
# root 用户创建 service
sudo vim /etc/systemd/system/new-project.service

内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=New Project Application
After=network.target

[Service]
Type=simple
User=newproject-app
WorkingDirectory=/home/newproject-app/new-project
ExecStart=/usr/bin/npm start
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

启用服务:

1
2
3
4
sudo systemctl daemon-reload
sudo systemctl enable new-project
sudo systemctl start new-project
sudo systemctl status new-project

3.5 端口管理辅助脚本

创建一个快速查询脚本:

1
vim ~/check-port.sh

内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# 快速查看端口占用情况

if [ -z "$1" ]; then
echo "用法: $0 <端口号>"
echo "示例: $0 8001"
exit 1
fi

PORT=$1
sudo ss -tulnp | grep ":$PORT "

if [ $? -eq 0 ]; then
echo ""
echo "✅ 端口 $PORT 已被占用"
else
echo ""
echo "✅ 端口 $PORT 空闲"
fi

添加执行权限:

1
chmod +x ~/check-port.sh

使用:

1
~/check-port.sh 8001

创建端口冲突检测脚本:

1
vim ~/scan-conflicts.sh

内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
# 扫描 8000-8999 段的占用情况

echo "扫描 8000-8999 端口占用情况..."
echo ""

for port in {8000..8999}; do
if sudo ss -tulnp | grep -q ":$port "; then
service=$(sudo ss -tulnp | grep ":$port " | awk '{print $7}')
echo "端口 $port: ✅ 占用 - $service"
fi
done
echo ""
echo "未列出的端口均为空闲"

3.6 Docker 部署的端口管理(可选)

如果用 Docker,配置略有不同(Compose V2 已忽略 version 字段,会提示 obsolete 警告,直接省略不写):

docker-compose.yml 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
services:
app:
image: your-image:latest
container_name: new-project
ports:
- "127.0.0.1:8005:8000" # 只绑定 localhost
environment:
- NODE_ENV=production
restart: unless-stopped

db:
image: postgres:15
container_name: new-project-db
# 不暴露端口,仅容器网络访问
environment:
POSTGRES_PASSWORD: your-password
volumes:
- pgdata:/var/lib/postgresql/data
restart: unless-stopped

volumes:
pgdata:

💡 Docker 端口绑定技巧:

  • 127.0.0.1:8005:8000:仅允许本地访问
  • 8005:8000:允许外部访问(不推荐)
  • 数据库服务不要 ports 映射,通过 Docker 内部网络访问

4、总结

端口规划检查清单

部署新项目前,按这个清单过一遍:

检查项 命令/说明 预期结果
端口已分配 grep 8005 ~/port-registry.md 有记录
端口未被占用 ss -tulpn | grep 8005 显示空闲
应用监听本地 ss -tulpn | grep 8005 127.0.0.1:8005
nginx 配置正确 sudo nginx -t syntax is OK
防火墙规则 sudo ufw status 仅开放 80/443/SSH
SSL 证书有效 curl -I https://api.example.com 200 OK
进程已守护 pm2 status / systemctl status running

关键要点

  1. 端口按段分配:8000-8999 生产、9000-9999 临时,便于记忆和管理
  2. 统一入口:nginx 反向代理,对外只暴露 80/443
  3. 本地隔离:应用和数据库只监听 127.0.0.1
  4. 文档化:维护端口清单,团队共享
  5. 自动化:用 systemd/PM2/docker 守护进程

安全最佳实践

  1. 防火墙最小化:UFW 只开放 80、443、SSH
  2. 数据库不外联:3306/5432 仅监听 localhost
  3. SSL 全覆盖:用 Let’s Encrypt 免费证书
  4. 定期审计:每月查看 ss -tulpn,清理僵尸端口
  5. 日志监控:tail -f /var/log/nginx/access.log 定期检查异常访问
多维实测与判断标尺矩阵:总结 检查项 命令/说明 预期结果 端口已分配 grep 8005/port-registry.md 有记录 端口未被占用 ss-tulpn\ grep 8005 应用监听本地 ss-tulpn\ grep 8005 nginx配置正确 sudo nginx-t syntax is OK

5、异常记录

Q1:nginx 启动失败,提示 port already in use?

现象:

1
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)

排查步骤:

1
2
3
4
5
6
7
# 查看谁占用了 80 端口
sudo ss -tulnp | grep :80

# 常见原因:
# 1. Apache 占用:sudo systemctl stop apache2
# 2. 旧 nginx 进程:sudo systemctl kill nginx
# 3. 其他应用:kill 掉对应进程

解决后重启 nginx:

1
sudo systemctl start nginx

Q2:访问域名显示 502 Bad Gateway?

可能原因:

  1. 后端应用未启动
1
2
3
4
5
# 检查应用是否在运行
~/check-port.sh 8005

# 如果没有运行,启动应用
sudo systemctl start new-project
  1. 应用监听地址错误
1
2
3
4
# 检查监听地址
sudo ss -tulnp | grep 8005

# 正确应该是 127.0.0.1:8005,如果显示 0.0.0.0:8005,修改应用配置
  1. nginx 配置端口错误
1
2
3
4
# 检查 nginx 配置
cat /etc/nginx/sites-available/new.example.com | grep proxy_pass

# 确认是 proxy_pass http://127.0.0.1:8005;

Q3:SSL 证书申请失败?

常见错误:

1
Failed to connect to xx.xx.xx.xx port 80: Connection refused

原因:80 端口未对外开放或被防火墙拦截

解决:

1
2
3
4
5
6
7
8
9
10
11
# 检查防火墙
sudo ufw status | grep 80

# 如果没有,添加规则
sudo ufw allow 80/tcp

# 检查 nginx 是否监听 80
sudo ss -tulnp | grep :80

# 重新申请
sudo certbot --nginx -d new.example.com

Q4:端口冲突,两个项目争抢一个端口?

场景:新项目想用 8005,但发现已被占用

解决:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 找出占用者
sudo ss -tulnp | grep :8005

# 查看 port-registry.md,找出临近空闲端口
cat ~/port-registry.md | grep 800

# 假设 8008 空闲,修改应用配置
vim .env
# PORT=8008

# 重启应用
sudo systemctl restart new-project

# 更新 nginx 配置
sudo vim /etc/nginx/sites-available/new.example.com
# 改 proxy_pass http://127.0.0.1:8008;

# 重载 nginx
sudo systemctl reload nginx

# 更新端口清单
vim ~/port-registry.md

Q5:Docker 容器无法访问宿主机数据库?

现象:容器内 localhost:3306 连接失败

原因:容器内 localhost 指容器本身,不是宿主机

解决:

1
2
3
4
5
6
7
8
9
10
11
12
# 方法 A:使用 host 网络模式
services:
app:
network_mode: "host"

# 方法 B:使用 host.docker.internal(Docker 20.10+ 推荐)
services:
app:
extra_hosts:
- "host.docker.internal:host-gateway"
# 然后在应用中连接 host.docker.internal:3306
# 旧版本可用 network_mode: "host"(会失去网络隔离)

或者让数据库也在 Docker 中,通过容器网络访问:

1
2
3
4
5
6
7
8
9
10
11
services:
app:
depends_on:
- db
environment:
- DB_HOST=db
- DB_PORT=5432

db:
image: postgres:15
# 不映射端口,容器内访问

Q6:忘记记录端口,不知道哪些端口在用?

快速扫描脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 创建扫描脚本
cat > ~/port-scan.sh <<'EOF'
#!/bin/bash
echo "=== 8000-8999 端口占用情况 ==="
echo ""
for port in {8000..8999}; do
if sudo ss -tulnp | grep -q ":$port "; then
process=$(sudo ss -tulnp | grep ":$port " | awk '{print $6, $7}')
printf "端口 %4d: ✅ %s\n" "$port" "$process"
fi
done
EOF

chmod +x ~/port-scan.sh

使用:

1
~/port-scan.sh
问题现场解剖:异常记录 ⚠️ 从问题出发:表面症状是如何一步步演变为生产故障或降权的? 独立开发者工具箱 · CH-56 · 核心卡点传导链 1. 后端应用未启动 bash 需通过真实环境实测核验 症结:认知偏差与配置盲区 2. 应用监听地址错误 bash 需通过真实环境实测核验 传导:隐性劣化与边界失守 3. nginx配置端口错误 bash 需通过真实环境实测核验 破局:量化指标与工程验证 ✅ 破局核心原则(结论先行) 一台VPS想稳定跑几十个项目,抓住一条主线:对外只开80/443两个口,全交给 nginx按域名分流;内部所有服务一律绑127.0.0.1,端口分段占位(8000-8999…

6、推荐工具与服务

底层机制与架构拆解:推荐工具与服务 四阶段递进分析模型 · 从前置识别到最终工程定级 S1 VPS提供商 https: //ittinker.com/go/vps… S2 域名注册 https: //ittinker.com/go/domain S3 监控工具 Uptime Kuma(自建监控 看板) S4 日志管理 Grafana Loki(轻量级日 志聚合) 💡 工程启示:任何一个前置环节失守,都会通过传导链放大为下游的系统性偏差

7、FAQ(端口规划高频问答)

Q1:一台 VPS 跑 50 个项目,端口段够用吗?

够用。光 8000-8999 就有 1000 个端口,按"一项目一端口"能塞 1000 个项目。真正的瓶颈通常是 CPU 和内存,端口数量从来不是限制。50 个项目的麻烦在资源,不在端口。

Q2:多项目同机,子域名(app1.com、app2.com)还是路径(/app1、/app2)?

独立项目优先用子域名,nginx 用 server_name 区分,各自独立 SSL,互不干扰。路径方式容易被前端静态资源、cookie path、CORS 坑到。只有同一个应用的多个模块才适合用路径(如 /api、/admin)。

Q3:SSH 端口要改默认 22 吗?

建议改(清单里 SSH 改成 22000 就是这意思)。改完务必同步放行:sudo ufw allow 22000/tcp,再 sudo ufw reload,同时确认云厂商安全组也放开了新端口,否则改完 ssh 上不去只能进控制台救。改端口能挡掉绝大多数自动扫描的暴力破解脚本,零成本。

Q4:临时服务(9000-9999)跑完忘了关,被扫到怎么办?

两条铁律。第一,临时服务也走 nginx + SSL,别图省事直接 ufw allow 9000 裸暴露。第二,UFW 默认 deny,只放行 80/443/SSH,这样就算忘了关应用,外部也连不进来。再配合定期跑 ss -tulpn 清理僵尸端口。

Q5:4 核 8G 真的能跑 50 个项目吗?什么时候该拆机?

看负载类型。50 个低流量静态站、小 API、定时任务,4 核 8G 扛得住,单个项目峰值常驻几十 MB 内存。一旦出现 2-3 个吃内存的大户(LLM 推理、大数据库查询、爬虫),就该拆。信号很明确:htop 常驻内存长期超 80%,或 dmesg -T | grep -i oom 出现 OOM killer,就该加内存或把大户迁到第二台 VPS。

问题整改前后解剖:FAQ(端口规划高频问答) ⚠️ 整改前:常见踩坑与失分现场 • 盲区 1:忽视「首屏与入口响应」边界约束 • 盲区 2:缺乏「边界容错与防线」实测核验 • 盲区 3:遗漏「可验证证据链」兜底监控 后果:线上隐性故障频发 / 评估等级下调 ✅ 整改后:高确定性工程实践 • 动作 1:首屏与入口响应:剥离冗余阻塞,核心功能与结… • 动作 2:边界容错与防线:显式设置超时熔断与异常输入… • 动作 3:可验证证据链:配备实机压测指标、日志与可复… 收益:指标可观测、配置可复现、风险可阻断

下一步

下一篇我们讲 《多项目 VPS 的自动化备份策略》,教你如何:

  • 用 rsync 增量备份代码和数据库
  • 自动化快照 + 异地容灾
  • 备份验证与恢复演练

独立出海,从基础设施开始。


💡 透明声明:本文包含 10 处推广链接(5 个独立服务,如 Vultr / Cloudflare / Tailscale 等)。每次点击我会收到一笔佣金,不影响你的购买价格,也不影响我的内容判断,工具好不好用我自己用了才知道。完整推广政策见 ittinker.com/about。

底层机制与架构拆解:下一步 四阶段递进分析模型 · 从前置识别到最终工程定级 S1 环节01 用rsync增量备份代码和 数据库 S2 环节02 自动化快照+异地容灾 底层规则强约束 S3 环节03 备份验证与恢复演练 底层规则强约束 S4 确定性架构标准输出 沉淀为可一键复现的工程 配置基线 💡 工程启示:任何一个前置环节失守,都会通过传导链放大为下游的系统性偏差

推荐工具与服务


问题整改前后解剖:推荐工具与服务 ⚠️ 整改前:常见踩坑与失分现场 • 盲区 1:忽视「Vultr VPS」边界约束 • 盲区 2:缺乏「Hetzner Cloud」实测核验 • 盲区 3:遗漏「DigitalOcean Droplet」兜底监控 后果:线上隐性故障频发 / 评估等级下调 ✅ 整改后:高确定性工程实践 • 动作 1:Vultr VPS:https:… • 动作 2:Hetzner Cloud:https:… • 动作 3:DigitalOcean Droplet:https:… 收益:指标可观测、配置可复现、风险可阻断

❓ FAQ(网络/反代最常问)

Q:BBR 加速要重启服务器吗?启用后多久生效?
不用重启,改 sysctl 立即生效(sudo sysctl -p)。验证:sysctl net.ipv4.tcp_congestion_control 含 bbr + lsmod | grep bbr 有输出就 OK。

Q:Caddy vs Nginx 怎么选?出海独立开发者用哪个?
Caddy:自动 HTTPS、配置文件简单(10 行起 HTTPS)。Nginx:生态成熟、性能极致、配置复杂。出海首选 Caddy,自动 Let’s Encrypt 省事;高流量/复杂路由选 Nginx。

Q:Nginx Proxy Manager(NPM)能替代手写 Nginx 吗?
个人/小团队完全够用,Web UI 配反代 + 自动 Let’s Encrypt + Let’s Encrypt DNS 验证。限制:复杂 rewrite / 动态 upstream / WAF 规则不行,还得手写 Nginx。

Q:Let’s Encrypt 证书多久续期一次?会自动续吗?
90 天有效期,certbot 默认 systemd timer / cron 每天跑 2 次检查,剩余 <30 天自动续。验证:sudo certbot renew --dry-run。坑:DNS-01 验证需要 API token,acme.sh 更稳。

Q:服务器被墙了怎么判断?多久能恢复?
ping + mtr + 国内外多地拨测综合判断(单一节点误判多)。真墙不可逆,工单申诉 95% 无效。预防:Cloudflare 代理 + 域名分流 + 多 IP 备份。应急:换 IP 或上 Cloudflare WARP。

底层机制与架构拆解:❓FAQ(网络/反代最常问) 四阶段递进分析模型 · 从前置识别到最终工程定级 S1 输入识别与场景分流 明确核心业务诉求与上下 文边界条件 S2 底层协议与规则校验 对照核心规范与真实负载 指标逐项核验 S3 瓶颈隔离与风险阻断 剔除干扰链路、单点隐患 与高危配置 S4 确定性架构标准输出 沉淀为可一键复现的工程 配置基线 💡 工程启示:任何一个前置环节失守,都会通过传导链放大为下游的系统性偏差

数据与引用

  • Cloudflare Registrar .com 2026 年批发价 $10.44/年,无差价、不赚溢价,续费价 = 首年价,比 Namecheap 续费 $18.48/年便宜约 44%。
    来源:StackScored - Cloudflare Registrar Pricing 2026
    含义:本文推荐域名+DNS 都走 Cloudflare 的核心原因,长期总账真的省,5 个域名 5 年能省 $290。
  • Namecheap .com 续费 2026 年 $18.48/年(首年 $9.98 促销),Porkbun 续费 $11.81/年且保持稳定:Namecheap .io 续费 $75.98/年,几乎是首年价两倍。
    来源:AffMaven - Porkbun Vs Namecheap 2026
    含义:算 3 年总账时 Namecheap 比 Porkbun 多花约 $20(按 .com),10 个域名 × 3 年就差 $200,不是小数。
  • Linux 系统临时端口(ephemeral)默认范围 32768-60999,用 sysctl net.ipv4.ip_local_port_range 可查,所以长期服务放在 8000-8999 才能避开系统出站连接占用的临时端口。
    来源:Linux man page - ip(7) - IP_LOCAL_PORT_RANGE
    含义:本文"分段到 8000-8999"是工程理由而非拍脑袋;这一段在出站连接繁忙时会大量占用,避开它能防止"自己服务占了自己的端口"。
  • VPS 市场预计 2026 年达 $6.43B,2035 年涨至 $25.22B(CAGR 16.40%),单台 VPS 跑多项目是最常见的部署模式之一:北美占 34.5%、欧洲 27.2%、亚太增速最快 16.9%。
    来源:MarketResearchFuture - Virtual Private Server Market 2026-2035
    含义:本文"用端口段管理多项目"是行业主流做法,不是野路子;VPS 规模和项目数都在涨,端口规划的需求只会更普遍。
  • Nginx 2025 年仍是 Top 1000 万网站中占有率最高的反向代理(38%+ 站点用 nginx,含直接和反代两种角色):本文把 nginx 作为统一入口是符合行业标准的选型。
    来源:W3Techs - Usage statistics of web servers
    含义:选 nginx 做统一反代不会过时,配置语法和模块生态都成熟,社区资料多,出问题容易搜到答案。
  • Hetzner Cloud 2026 年 6 月 15 日对 CPX/CCX 涨价 144-176%(新订单生效),CCX63 从 €374.49 涨到 €853.49/月(+127.9%);老 CX 涨 33-38% 较温和。
    来源:Northflank - Hetzner cloud server price increases in 2026
    含义:在 Hetzner 上跑"大机器切多项目"模式时,按新价算预算;老 CX 实例按原合同价继续跑,迁移前算清楚。

本文最后更新:2026年8月

独立开发者工具箱系列,欢迎关注!

问题整改前后解剖:数据与引用 ⚠️ 整改前:常见踩坑与失分现场 • 盲区 1:忽视「Cloudflare Registrar…」边界约束 • 盲区 2:缺乏「Namecheap.com续费2026…」实测核验 • 盲区 3:遗漏「Linux系统临时端口(…」兜底监控 后果:线上隐性故障频发 / 评估等级下调 ✅ 整改后:高确定性工程实践 • 动作 1:Cloudflare Registrar.com 2026年批发价$… • 动作 2:Namecheap.com续费2026年$18.48/年(首年$… • 动作 3:Linux系统临时端口(ephemeral)默认范围… 收益:指标可观测、配置可复现、风险可阻断