生产环境禁用 nohup go run main.go &,必须用 systemd 托管 go build 编译的二进制;需配置 restart、日志重定向、环境变量,并手动处理 sigterm 实现优雅关机。

直接上结论:别用 nohup go run main.go & 部署 Gin 项目,它只是“后台进程”,不是真正的守护进程;生产环境必须用 systemd 管理,或至少确保双 fork + setsid() + 标准流重定向。
为什么 nohup go run main.go & 不算守护进程
它确实能躲过终端关闭(SIGHUP),但仍有严重隐患:
-
go run是编译+运行两步,每次启动都临时生成二进制,浪费 CPU、无调试符号、无法做安全加固 - 进程仍属于原 shell 会话,
ps -o pid,ppid,sid,tty查看时tty是 ?,但sid和父 shell 相同,未真正脱离会话 - 标准输出/错误默认写入
nohup.out,日志无轮转、无权限控制、容易撑爆磁盘 - 崩溃后不会自动拉起,没有健康检查、资源限制、依赖管理
推荐做法:用 systemd 托管已编译的二进制
Gin 项目应先 go build -o gin-api main.go 得到静态二进制,再交由 systemd 管理。这是当前 Linux 生产环境事实标准。
- 创建服务文件:
/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 原生不提供跨平台守护封装,github.com/sevlyar/go-daemon 已归档,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 上,卸载时进程会被阻塞 - 不要在守护化之后再
log.SetOutput(...)—— 日志库可能已缓存了旧的os.Stdout句柄
最容易被忽略的点:Gin 的 DebugMode 和信号处理
即使进程成功 daemon 化,Gin 默认行为仍可能破坏稳定性:
-
GIN_MODE=debug会开启实时模板重载和 panic 捕获页面 —— 这些功能依赖 stdin/stdout,且暴露敏感信息,线上必须设为release - Gin 不处理
SIGTERM,需手动注册:signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT),然后优雅关闭 HTTP server - 若用
gin.Default(),其自带的 recovery 中间件会把 panic 写到os.Stderr—— 守护化后若没重定向 stderr,panic 就丢进了黑洞











