go应用在fly.io上运行失败主因是fly.toml配置错误:[env]必须显式声明所有环境变量(如port),[services]中internal_port须与go监听端口完全一致且为整数,build块应使用dockerfile而非image,需添加/flycheck健康检查路由,无关字段如release_command、[vm]等应删除。

怎么写对 fly.toml 的 [env] 和 [services]?
Go 应用在 Fly.io 上跑不起来,八成是这两个块配错了。Fly 不会自动读取 .env 或解析 Go 的 os.Getenv 以外的环境来源——所有变量必须显式写进 [env],否则 os.Getenv("PORT") 就是空字符串。
服务端口尤其关键:[services] 里的 internal_port 必须和 Go 里 http.ListenAndServe(":8080", nil) 的端口号完全一致(默认是 8080),不能只写 80 或留空;Fly 的反向代理只把流量打到这个端口,写错就 502。
-
[env]中的变量名区分大小写,DB_URL和db_url是两个变量 -
internal_port值不能是字符串,必须是整数(如8080,不是"8080") - 如果 Go 程序监听
0.0.0.0:8080,但internal_port = 3000,请求永远进不来
build 块里用 image 还是 dockerfile?
绝大多数 Go 项目该用 dockerfile = "Dockerfile",而不是 image。Fly 的构建流程会先运行 docker build,再推镜像、部署。如果你直接填 image = "myorg/myapp:latest",Fly 就跳过构建,直接拉取远程镜像——这意味着本地改了代码、没推镜像,部署的就是旧版本。
更隐蔽的问题是:Fly 默认的构建环境没有 go 命令,所以不能用 builder = "remote" 配合 buildpacks 来编译 Go(它不支持 Go buildpack)。必须自己写 Dockerfile 做多阶段构建。
- 推荐 Dockerfile 头部用
FROM golang:1.22-alpine AS builder,最后用FROM alpine:latest运行二进制 -
fly.toml中build块只需写dockerfile = "Dockerfile",其他字段可省略 - 如果误删了
dockerfile行,Fly 会尝试用 buildpacks,结果报错no buildpacks found
HTTP health check 路由为什么老失败?
Fly 的健康检查默认发 GET 请求到 /flycheck,但 Go 的 http.ServeMux 不会自动处理这个路径。如果你没显式注册,就会返回 404,然后 Fly 把实例标为 unhealthy,反复重启。
解决方式不是关掉健康检查(不推荐),而是让 Go 程序响应它。注意:不要用重定向(302),必须是 200;也不要用 http.Redirect,它默认发 302。
- 加一行
http.HandleFunc("/flycheck", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) }) - 别写成
http.HandleFunc("/flycheck/", ...)(末尾斜杠不匹配) - 如果用了 Gin/Echo 等框架,确保路由注册在健康检查路径上,且中间件不拦截或修改状态码
fly.toml 里哪些字段其实可以删掉?
新用户常从模板复制一堆字段,比如 [deploy] 下的 release_command、strategy,或 [metrics]。这些字段在 Go 应用中基本用不到,留着反而容易引发误解或错误行为。
最典型的是 release_command:它会在每次部署后单独起一个容器执行命令(如数据库迁移)。但 Go 二进制本身不带 shell,也没 psql,硬加这行只会导致部署卡住、超时失败。
- 干净的 Go 项目
fly.toml只需保留app、primary_region、build、[env]、[services]和[http_service] -
[[vm]]块是旧版配置,新版已弃用,写了会报错unknown field "vm" -
auto_deploy = true在[deploy]下看似方便,但 CI/CD 场景下可能绕过测试直接上线,建议手动fly deploy
真正麻烦的从来不是写多少,而是删干净——多一个没用的字段,就多一个未来排查时分心的点。










