生产环境必须用 systemd 管理已编译的 Gin 二进制,禁用 nohup & 和其他守护工具;需配置 Restart、Environment、WorkingDirectory 等关键字段,并手动验证权限、配置路径与端口占用。

直接用 systemd 管理 Gin 进程最稳妥
别碰 supervisor、pm2 或 nohup &,Gin 是纯二进制可执行文件,systemd 原生支持重启策略、日志归集、资源限制和依赖管理,生产环境唯一推荐方式。宝塔面板里的“Go项目管理器”底层也是调用 systemd,但封装层容易掩盖配置细节,出问题反而难排查。
关键点在于:Gin 本身不提供进程守护能力,它只负责 HTTP 服务逻辑;守护必须由操作系统级工具接管。
- 确保你的 Gin 应用已编译为静态链接二进制(
CGO_ENABLED=0 go build -a -o app),避免运行时缺 libc 依赖 - 二进制文件路径必须绝对且可执行(
chmod +x /opt/myapp/app) - 工作目录(
WorkingDirectory)要设为配置文件、日志目录所在路径,否则viper.ReadInConfig()会失败 - 不要在 service 文件里写
ExecStart=/bin/sh -c "cd /opt/myapp && ./app"—— systemd 不解析 shell 语法,会直接报failed at step EXEC spawning
systemd service 文件必须包含的字段
把下面内容保存为 /etc/systemd/system/mygin.service,注意替换 myapp 和路径:
[Unit] Description=My Gin API Service After=network.target [Service] Type=simple User=www-data WorkingDirectory=/opt/myapp ExecStart=/opt/myapp/app Restart=always RestartSec=5 Environment="GIN_MODE=release" Environment="VIPER_CONFIG=/opt/myapp/config.yaml" [Install] WantedBy=multi-user.target
说明:
-
Type=simple:Gin 启动后就进入主循环,不是 fork 模式,不能用 forking -
Restart=always:崩溃、OOM、信号退出都会触发重启;若只想在崩溃时重启,改用on-failure -
Environment用于透传环境变量,比在代码里os.Setenv更可靠,也方便不同环境复用同一二进制 - 别加
KillSignal=SIGTERM—— Gin 默认已注册http.Server.Shutdown处理 SIGTERM,加了反而可能干扰优雅退出
启动前必须验证的三件事
跑 systemctl start mygin 之前,先手动验证二进制能否正常加载配置并监听端口:
- 切到目标用户执行:
sudo -u www-data /opt/myapp/app,看是否打印listening and serving HTTP on :8080且无 panic - 检查配置路径是否可读:
sudo -u www-data ls -l /opt/myapp/config.yaml,权限至少是644 - 确认端口未被占用:
sudo ss -tulpn | grep ':8080',避免bind: address already in use导致反复重启
如果手动运行成功但 systemd 启动失败,90% 是 WorkingDirectory 或 User 权限问题,用 journalctl -u mygin -f 看第一行错误,重点关注 permission denied 或 no such file or directory。
Docker 部署时别自己搞守护
容器内不需要 systemd —— 容器本质就是一个进程的隔离环境,ENTRYPOINT ["./app"] 就是 PID 1,Docker daemon 自己负责重启和健康检查。强行在容器里跑 systemd 会引入额外复杂度,还可能因 init 进程缺失导致信号转发异常。
正确做法:
- Dockerfile 用多阶段构建,最终镜像只含静态二进制和配置文件
- 用
docker run --restart=unless-stopped或deploy.restart.policy在 compose 中定义重启策略 - 健康检查走 HTTP:
healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/healthz"],别用ps aux | grep app
systemd 的 RestartSec 和 Docker 的 restart 策略看似重复,实则作用域完全不同:前者管单机进程生命周期,后者管容器实例调度。混用或忽略任一环节,都会让故障恢复变不可控。


















