生产环境部署 gin 项目必须用 systemd 托管编译后的二进制,禁用 nohup go run;因后者无法脱离会话、无重启机制、无日志轮转、无资源限制,且每次启动重复编译。

别用 nohup go run main.go & 部署 Gin 项目——它根本不是守护进程,只是个“假装活着”的后台进程,生产环境必须用 systemd 托管已编译的二进制。
为什么 nohup go run main.go & 不行
它确实能躲过终端关闭(SIGHUP),但有四个硬伤:
-
go run每次启动都临时编译,浪费 CPU、无调试符号、无法做安全加固 - 进程仍属于原 shell 会话:
ps -o pid,ppid,sid,tty显示sid和父 shell 相同,未真正脱离会话 - 日志默认写入
nohup.out,无轮转、无权限控制,磁盘容易被撑爆 - 崩溃后不自动重启,没健康检查、没资源限制、没依赖管理
正确做法:用 systemd 托管 go build 二进制
这是当前 Linux 生产环境事实标准。步骤很明确:
- 先执行
go build -o gin-api main.go得到静态二进制,放到固定路径如/opt/gin-api/gin-api - 创建服务文件
/etc/systemd/system/gin-api.service,关键字段不能少:[Unit] Description=Gin API Service After=network.target [Service] Type=simple User=www-data WorkingDirectory=/opt/gin-api ExecStart=/opt/gin-api/gin-api Restart=always RestartSec=5 LimitNOFILE=65536 StandardOutput=journal StandardError=journal Environment="GIN_MODE=release" Environment="PORT=8080" [Install] WantedBy=multi-user.target
- 启用并启动:
sudo systemctl daemon-reload && sudo systemctl enable --now gin-api - 查状态:
sudo systemctl status gin-api;看日志:journalctl -u gin-api -f
如果非要手写守护逻辑(仅限学习或极简场景)
Go 原生不提供跨平台守护封装,golang.org/x/sys/unix.Setsid() 是唯一可靠入口,但必须满足三个硬性条件:
- 必须 fork 一次,父进程调
os.Exit(0),子进程再调unix.Setsid()——否则setsid()会失败(要求调用者不能是会话组长) - 必须显式重定向
os.Stdin/os.Stdout/os.Stderr到/dev/null或日志文件,否则可能卡住或写满根分区 - 必须
os.Chdir("/"),否则工作目录若挂载在 USB 或 NFS 上,卸载时进程会被阻塞
真正麻烦的不是写几行 fork + setsid,而是日志轮转、OOM killer 对策、信号转发、资源隔离这些 systemd 已经帮你兜底的事——自己补全,成本远高于学懂 systemd 配置。











