这是「把自己部署到互联网上」系列的第 34 篇。
上一篇复盘了从 HTTPS 到对象存储,再到 Docker 部署和数据库初始化。博客具备上线条件后,很快会遇到一个实际维护问题:我想用 Navicat 看一下 Docker 里的数据库,应该怎么连?
这个需求很常见。比如确认表是否创建成功,检查迁移有没有执行,看管理员账号是否初始化,排查某条数据为什么没写进去,导出部分数据,或者临时执行一条 SQL。Navicat、DataGrip、DBeaver 这类工具确实方便。
但前提必须明确:不要为了让数据库工具连上,就直接把数据库端口暴露到公网。
MySQL 服务在哪儿?
先看 Docker 里的数据库在哪里。如果博客用 Docker Compose 部署,数据库通常是一个单独服务:
services:
app:
...
db:
image: mysql:8.0
应用容器连接数据库时,通常写:
DB_HOST=db
DB_PORT=3306
这是 Docker 内部网络通信。
应用容器和数据库容器在同一个 Compose 网络里,所以应用能通过 db:3306 找到数据库。
但你的本地电脑不在这个 Docker 网络里,Navicat 不能直接用 db 这个主机名连接。
也就是说,容器内部能访问,不代表本地电脑能访问。
要让本地工具连接 Docker 数据库,需要额外设计访问路径。
最不推荐的方式,是直接把数据库端口映射到宿主机公网:
ports:
- "3306:3306"
然后在云平台安全组里放行 3306,本地 Navicat 连接 服务器公网 IP:3306。
这确实能连,但风险很高。
数据库端口一旦暴露公网,就可能被扫描、爆破和探测。
如果密码弱、账号权限大或数据库版本有漏洞,风险会直接放大。
服务器里最常见的问题,就是临时开放后忘记关闭。
SSH 隧道建立
我的原则是:数据库默认不暴露公网。本地工具连接优先使用 SSH 隧道。
SSH 隧道的思路是:Navicat 不直接连公网数据库端口,而是先通过 SSH 连接服务器,再把本地端口转发到服务器内部数据库端口。路径是:
Navicat 本地端口
-> SSH 隧道
-> 服务器
-> Docker 数据库
这样公网只保留 SSH 管理入口,数据库端口不直接暴露。
比较实用的做法,是把数据库端口只绑定到服务器本地地址。例如 Compose 里写:
db:
image: mysql:8.4
restart: always
container_name: db
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
env_file:
- .env
ports:
- "127.0.0.1:3306:3306"
volumes:
- /data/blog/mysql:/var/lib/mysql
这表示把数据库容器的 3306 映射到宿主机的 127.0.0.1:3306。
服务器本机可以访问,但公网不能直接访问。
修改 Compose 后启动数据库服务:
docker compose up -d --force-recreate db
确认监听:
sudo ss -lntp | grep 3306
如果看到 127.0.0.1:3306,说明只监听本地地址。
然后在本地电脑建立 SSH 隧道。假设服务器 IP 是 1.2.3.4,SSH 用户是 coduty,服务器本机数据库端口是 13306,本地也使用 13306:
ssh -L 3306:127.0.0.1:3306 coduty@1.2.3.4
这表示把本地 127.0.0.1:13306 转发到服务器上的 127.0.0.1:3306。Navicat 里填写:
Host: 127.0.0.1
Port: 3306
User: 数据库用户名
Password: 数据库密码
Database: 数据库名
注意,这里填的是本地地址和本地端口,不是服务器公网 IP 的数据库端口。
SSH 隧道负责把流量转到服务器内部。

数据库工具连接
Navicat 也可以直接配置 SSH 隧道。通常要填两部分。数据库信息里,Host 是服务器视角下的数据库地址。如果数据库端口绑定到服务器本机,就填:
Host: 127.0.0.1
Port: 13306
SSH 信息里,填写服务器公网 IP、SSH 端口、SSH 用户,以及密码或私钥。这里最容易混的是数据库 Host:使用 Navicat 内置 SSH 时,它连接的是 SSH 登录到服务器后的地址;使用命令行 ssh -L 时,Navicat 连接的是你本机的 127.0.0.1:本地端口。

如果数据库没有映射到宿主机,只存在 Docker 内部网络里,Navicat 内置 SSH 通常不能直接访问
db:3306,因为db是 Docker 网络里的服务名,不是宿主机能解析的主机名。这时要么绑定到服务器本地地址,要么直接进容器操作。
简单检查
简单检查其实不一定要 Navicat 或者其他工具,直接进入容器命令行操作。
可以直接进入数据库容器:
docker compose exec db mysql -u blog_user -p 这里是密码
如果是 PostgreSQL:
docker compose exec db psql -U blog_user -d 这里是密码
这种方式不需要开放任何端口。缺点是交互体验不如图形化工具,但用于简单查看表、确认数据、执行少量 SQL,足够安全直接。
如果确实必须临时开放公网端口,至少要做到:
- 只允许自己的固定公网 IP 访问,不对
0.0.0.0/0开放 - 使用低权限数据库账号和强密码
- 用完关闭安全组规则
- 删除或恢复 Compose 端口映射
- 检查数据库日志
配置完后,要确认数据库没有暴露公网。先看 Docker 映射:
docker ps
如果看到:
0.0.0.0:3306->3306/tcp
就要警惕,说明端口绑定到了所有网卡。如果看到:
127.0.0.1:13306->3306/tcp
说明只绑定本机地址,相对更安全。
再看系统监听:
sudo ss -lntp | grep 3306
sudo ss -lntp | grep 3306
同时检查云平台安全组和系统防火墙,确认没有把数据库端口对公网开放。
如果没有建立 SSH 隧道,却能直接连服务器公网 IP 的数据库端口,说明数据库可能已经暴露,需要立刻修改服务端口或密码。
常见问题
Navicat 连接被拒绝
先确认 SSH 隧道是否还在运行,本地端口是否写对,服务器本机端口是否监听,数据库容器是否运行。
如果错误是 access denied,通常是账号密码或权限问题,而不是网络不通。
能连上但看不到数据库,说明账号权限不足
这不一定是坏事。远程维护工具不应该默认使用 root 账号。更稳的方式是创建专门维护账号,只授予必要数据库的必要权限。
SSH 隧道断开后 Navicat 也断开,这是正常现象
如果通过命令行建立隧道,终端关闭后隧道就没了。可以保持终端打开,也可以使用 Navicat 内置 SSH。对个人博客维护来说,临时打开,用完关闭,通常更安全。
我的推荐操作顺序是:
- 简单查询直接进容器
- 需要图形化工具时,用 SSH 隧道
- 尽量不直接开放公网数据库端口
- 若临时开放,必须限制来源 IP,用完关闭
小结:
Navicat 如何连接 Docker 里的数据库?最稳的答案不是开放 3306,而是让数据库端口只在服务器内部可达,本地通过 SSH 隧道访问。这样既能满足维护需求,又不会把数据库变成公网入口。
下一篇继续写博客上线后的几个真实问题:防爬、静态资源和发文流程。数据库能维护只是一步,长期运营博客还要处理访问、资源和内容发布。
本文由 楸木 原创,转载请注明出处。