> ## Documentation Index
> Fetch the complete documentation index at: https://docs.withnubo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Go

> Ship a Go service to Nubo

If you already have a Go app, you only need three things to deploy:

1. A `go.mod` at the repo root.
2. A `main.go` (at the root, or under `cmd/<name>/main.go` for monorepos).
3. Your server listens on `:$PORT` and `0.0.0.0`.

That's it. Skip to [Deploy](#deploy) below.

## Starting from scratch

If you don't have a Go app yet, here's the smallest possible one:

```bash theme={null}
mkdir hello-go && cd hello-go
go mod init github.com/yourname/hello-go
```

`main.go`:

```go main.go theme={null}
package main

import (
    "fmt"
    "net/http"
    "os"
)

func main() {
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello from Go on Nubo!")
    })
    http.ListenAndServe(":"+port, nil)
}
```

Test it locally:

```bash theme={null}
PORT=8080 go run .
# open http://localhost:8080
```

Commit and push to GitHub:

```bash theme={null}
git init && git add . && git commit -m "init"
git remote add origin git@github.com:yourname/hello-go.git
git push -u origin main
```

## Deploy

<Steps>
  <Step title="Open the New Frame modal">
    In your Nubo dashboard, open the Project and Space you want, then hit **+ Frame**.
  </Step>

  <Step title="Pick the repo and branch">
    Connect your GitHub repo. The branch you pick is the one Nubo watches for pushes.
  </Step>

  <Step title="Confirm the port">
    Should match the port your app actually listens on (`8080` for the example above).
  </Step>

  <Step title="Deploy">
    Hit deploy. The first build takes 30 to 60 seconds. You can follow it in the Logs tab.
  </Step>
</Steps>

Once the build finishes, your app is live at a `*.nubo.onl` URL shown in the dashboard. To redeploy, push to your tracked branch.

## Monorepos and multiple binaries

If your repo has more than one binary (`cmd/api/main.go`, `cmd/worker/main.go`), Raypacks picks the alphabetically first one by default. You can point the build at a specific one with a root `nubo.toml`:

```toml nubo.toml theme={null}
[build]
entrypoint = "./cmd/api"
```

That file lives once at the repo root, though, so its `entrypoint` applies to every Frame built from that repo: a single committed `nubo.toml` can't hand two Frames two different binaries. To deploy several binaries as separate Frames, set each Frame's build **Source directory** to that binary's package (`cmd/api`, `cmd/worker`) under **Frame Settings** instead. Each Frame keeps its own URL, env vars, and deploy history.

## Things that trip people up

* **Hardcoded port.** `http.ListenAndServe(":8080", nil)` works only if your Frame's port is also `8080`. Read `os.Getenv("PORT")` to be safe.
* **Binding to `localhost`.** Listen on `0.0.0.0` (or empty host like `":8080"`), not `127.0.0.1`. Nothing outside the container can reach `localhost`.
* **Library repos.** If your repo has no `main.go` anywhere, Raypacks won't pick it up. You need a `package main` somewhere with a `main()`.
* **CGO.** The Go pack builds with `CGO_ENABLED=0`. If you depend on a C library, switch the Frame's Builder to **Paketo** for now.
* **Go version.** The Go pack builds on a fixed Go version. It doesn't read the `go` directive from your `go.mod`, so bumping `go 1.XX` there won't change the toolchain the build uses.

## How the build works

This guide uses the [Go pack](/raypacks/packs/go) of [Raypacks](/raypacks/overview), Nubo's default builder. It produces a static binary on a scratch image. No Dockerfile, no `go.mod` version pinning, no `BP_*` env vars.

If you need something the Go pack doesn't support yet (ARM, custom `-ldflags`, CGO), switch the Frame's Builder to **Paketo** in **Frame Settings**.
