Deploying Go on Hop3¶
A Go app on Hop3 is the simplest kind of long-running service: go build produces a single self-contained binary, Hop3 runs that binary, and the binary listens on a TCP port that nginx proxies to. There is no interpreter to install at runtime and no application server (no uWSGI, no Gunicorn) — the binary is the server. The framework you pick (Gin, Fiber, or the stdlib net/http) only changes how you write handlers; the deployment shape is identical.
This page covers what every Go framework on Hop3 shares. Read it first, then follow the per-framework guide below.
How Hop3 builds and runs Go¶
Hop3 detects a Go app from its go.mod and uses the Go toolchain to build it. The build happens on the server (or in the build container) on every deploy:
- Fetch & build — Hop3 runs your
before-buildcommand, which is justgo build. The recommended form strips debug info for a smaller binary:go build -ldflags='-s -w' -o <app> .. Go's module cache means dependencies are only re-downloaded whengo.mod/go.sumchange, so repeat builds are fast. - Run — Hop3 starts the binary named in
[run].start(e.g../hop3-tuto-gin) and supervises it as a worker. - Proxy — Hop3 assigns a dynamic port via the
$PORTenvironment variable and points nginx at it. Your binary must read$PORTand bind to it — a hardcoded port will not receive traffic. The universal idiom is:
port := os.Getenv("PORT")
if port == "" {
port = "8080" // local dev fallback
}
r.Run(":" + port) // or app.Listen(":" + port), or http.ListenAndServe(":"+port, ...)
A representative hop3.toml shared by every Go framework here:
[metadata]
id = "my-go-app"
version = "1.0.0"
[build]
before-build = ["go build -ldflags='-s -w' -o my-go-app ."]
packages = ["golang"]
[run]
start = "./my-go-app"
[port]
web = 8080
[healthcheck]
path = "/up"
timeout = 30
interval = 60
[port].web is the local fallback the tutorials bind to; in production Hop3 overrides it with $PORT. The [healthcheck] path should be a cheap endpoint that returns 200 (the tutorials use a tiny /up handler) so Hop3 can confirm the binary came up before sending traffic.
Cross-cutting notes¶
- Config via environment. Hop3 injects configuration as env vars; read everything through
os.Getenv. Set values withhop3 config set --app <app> KEY=value. The most important isHOST_NAME, which tells nginx which domain to serve — set it, then redeploy so nginx picks it up. - Addons are env vars too. Attaching a database or cache exposes a connection string: PostgreSQL via
DATABASE_URL, Redis viaREDIS_URL. Pass them straight to your driver, e.g.gorm.Open(postgres.Open(os.Getenv("DATABASE_URL")), ...)orredis.ParseURL(os.Getenv("REDIS_URL")). No host/port wiring on your side. - Release mode. Frameworks behave differently in dev vs. production. For Gin, set
GIN_MODE=release(the tutorial puts it in[env]) to disable debug logging and the dev banner. - Don't commit the binary. The binary is a build artifact rebuilt on every deploy — add it (and
vendor/) to.gitignore. - The deploy flow is the same for all of them.
hop3 deploy <app>(first deploy creates the app) →hop3 config set --app <app> HOST_NAME=<app>.<your-domain>→ deploy again to apply → verify withhop3 app statusand acurlof the healthcheck path. After that, manage withhop3 app logs,hop3 app restart,hop3 config show, andhop3 ps scale --app <app> web=N.
Choose a framework¶
| Framework | Tutorial | Good for |
|---|---|---|
| Gin | A full Gin REST API with health, info, and CRUD endpoints | The most popular Go web framework — batteries-included routing, JSON binding, middleware |
| Fiber | A Fiber app built on Fasthttp with logging, CORS, and recovery middleware | An Express-style API and maximum raw throughput |
Both produce a single binary that binds $PORT, so the hop3.toml and deploy steps are nearly identical — pick the framework whose API you prefer. Want a static site generated by Go (Hugo) instead of a service? That lives under Static Sites.