# How Do I Set Up a Go Dev Environment in a Cloud Sandbox?

> A new sandbox has Go 1.27.2 in /usr/local/go. Installing the current release from go.dev took 3 seconds. Modules cache in ~/go/pkg/mod and builds in ~/.cache/go-build, both on the sandbox disk, so a named snapshot cut a cold 87-second build of the GitHub CLI to 4.6 seconds.

Source: https://sandboxer.com/guides/go-dev-environment-in-a-cloud-sandbox. Updated 2026-10-27.

**Short answer:** A new sandbox has Go 1.27.2 in /usr/local/go. Installing the current release from go.dev took 3 seconds. Modules cache in ~/go/pkg/mod and builds in ~/.cache/go-build, both on the sandbox disk, so a named snapshot cut a cold 87-second build of the GitHub CLI to 4.6 seconds.

We ran every command on this page on prod on 2026-10-27 in eu-central, on `small` sandboxes (2 vCPU, 4 GB) and one `default` (4 vCPU, 8 GB) for the size comparison. Timings vary by host and neighbors. The project is [cli/cli](https://github.com/cli/cli), the GitHub CLI, at commit `9be5608`: about 400 MB of modules and 259 test packages, so the caches matter.

## Results

| Step | `small` | `default` |
| - | - | - |
| Install Go 1.27.3 from go.dev | 2.7 s | |
| `go build ./cmd/gh`, empty module and build cache | 87.4 s | |
| `go build`, warm module cache, empty build cache (2 runs) | 63.2, 63.3 s | 38.3, 35.2 s |
| `go build`, nothing changed | 0.3 s | |
| `go build`, one file changed | 3.1 s | |
| `go test ./...`, first run | 155 s | |
| `go test ./...`, again (results cached) | 1.5 s | |
| `go test -count=1 ./...`, warm build cache | 93.8 s | |
| Cross-compile `gh` for another OS/arch (3 targets) | 65 to 69 s each | |
| From the named snapshot: first `go build` | 4.6 s | |
| From the named snapshot: `go test ./...` | 3.1 s (257 of 259 cached) | |

The lesson: in Go the build cache is worth more than the module cache. Downloading modules was about 24 of the 87 cold seconds; compiling was the rest. Save both, and a new sandbox builds in seconds.

## 1. Check the Go that is already there

```bash
sbx new --name go-dev --type small
sbx exec sb_qvmfxnvkgt bash -lc "go version; which go; go env GOROOT GOPATH GOCACHE GOMODCACHE GOTOOLCHAIN; nproc"
```

```text
go version go1.27.2 linux/amd64
/usr/local/go/bin/go
/usr/local/go
/home/user/go
/home/user/.cache/go-build
/home/user/go/pkg/mod
auto
2
```

Go comes from the official tarball in `/usr/local/go`, not from apt. Both caches start empty and live under `/home/user`. That was the image on 2026-10-27; it changes over time. The full inventory is in [What's pre-installed in a sandbox](/guides/whats-preinstalled-in-a-sandbox).

## 2. Watch GOTOOLCHAIN=auto, then install the current release

cli/cli's `go.mod` says `toolchain go1.27.3`. With the default `GOTOOLCHAIN=auto`, the first `go` command in the repo fetched that toolchain by itself:

```bash
sbx exec sb_qvmfxnvkgt --timeout 300 bash -lc "git clone -q https://github.com/cli/cli && cd cli && git checkout -q 9be5608274ec && go version"
```

```text
go: downloading go1.27.3 (linux/amd64)
go version go1.27.3 linux/amd64
```

That works, but the toolchain lands in the module cache, and every repo that pins a newer Go repeats it. Installing the release itself is cleaner. The current stable release on [go.dev/dl](https://go.dev/dl/) on 2026-10-27 was go1.27.3. Check the SHA-256 from that page, then replace `/usr/local/go` as [the official install steps](https://go.dev/doc/install) say:

```bash
sbx exec sb_qvmfxnvkgt --timeout 300 bash -lc '
  curl -fsSLO https://go.dev/dl/go1.27.3.linux-amd64.tar.gz
  echo "65efbc80fdc1fa2cd2631cad5b481777a68e8725f34e89f50fc6ac714e379b40  go1.27.3.linux-amd64.tar.gz" | sha256sum -c -
  sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.27.3.linux-amd64.tar.gz
  go version; cd cli && GOTOOLCHAIN=local go version
  go clean -modcache'
```

```text
go1.27.3.linux-amd64.tar.gz: OK
go version go1.27.3 linux/amd64
go version go1.27.3 linux/amd64
```

It took 2.7 seconds. `GOTOOLCHAIN=local go version` proves the repo now builds with the installed Go and no download. `go clean -modcache` removed the auto-fetched toolchain so the next measurements start cold.

## 3. Build and test, cold and warm

Long builds run past the 30-second default of `sbx exec`, so we ran them with `--detach` and read the log. The script times each step with bash `time`:

```bash
sbx exec sb_qvmfxnvkgt --detach "bash -l bench1.sh > bench1.log 2>&1"
sbx exec sb_qvmfxnvkgt "cat bench1.log"
```

```text
== build, empty mod + build cache
87.416 s
394M	/home/user/go/pkg/mod
555M	/home/user/.cache/go-build
== build, warm mod cache, empty build cache
63.240 s
== build, nothing changed
0.339 s
== build, one file in pkg/cmd/pr/create changed
3.114 s
```

The second build ran after `go clean -cache`, so it shows the cost of compiling with every module already on disk. Then the tests:

```text
== go test ./..., first run
155.026 s
exit 0
…
go: downloading github.com/creack/pty v1.1.24
go: downloading gopkg.in/h2non/gock.v1 v1.1.2
…
== go test ./..., second run (results cached)
1.487 s
== go test -count=1 ./..., warm build cache
93.822 s
exit 0
396M	/home/user/go/pkg/mod
857M	/home/user/.cache/go-build
```

All 259 test packages passed. Two details matter for snapshots:

- The first test run downloaded eight more modules. They are test-only dependencies that `go build ./cmd/gh` never needs. Run `go mod download` or the full test suite before you save, or the snapshot misses them.
- `go test` caches passing results, so an unchanged package reports `(cached)`. `-count=1` forces the tests to run; it still reuses compiled packages.

## 4. Cross-compile and copy the binary out

Go builds for any OS and CPU from the sandbox. With `CGO_ENABLED=0` no C toolchain is involved:

```bash
cd ~/cli
for os_arch in darwin/arm64 windows/amd64 linux/arm64; do
  GOOS=${os_arch%/*} GOARCH=${os_arch#*/}
  out=dist/gh-$GOOS-$GOARCH; [ $GOOS = windows ] && out=$out.exe
  CGO_ENABLED=0 GOOS=$GOOS GOARCH=$GOARCH go build -trimpath -ldflags "-s -w" -o $out ./cmd/gh
done
file dist/*
```

```text
dist/gh-darwin-arm64:      Mach-O 64-bit arm64 executable, flags:<|DYLDLINK|PIE>
dist/gh-linux-arm64:       ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, …
dist/gh-windows-amd64.exe: PE32+ executable (console) x86-64, for MS Windows, 8 sections
```

Each target took 65 to 69 seconds on `small`. The build cache is keyed by target, so the first build for a new GOOS/GOARCH is a cold build even when the native one is warm. Copy a binary to your machine with `sbx scp`:

```bash
sbx scp sb_qvmfxnvkgt:/home/user/cli/dist/gh-darwin-arm64 .
sha256sum gh-darwin-arm64
```

```text
a3bda216ea135c3de256488eab31ec2ecd36a116cc155c6ddf453426ab9cfe21  gh-darwin-arm64
```

The 38 MB file arrived in 22 seconds with the same hash as in the sandbox. We checked the file type, not a run on a Mac.

## 5. gopls over SSH

Install the language server in the sandbox. It goes to `~/go/bin`, which the login `PATH` already includes:

```bash
sbx exec sb_qvmfxnvkgt --timeout 600 bash -lc "go install golang.org/x/tools/gopls@latest && gopls version"
```

```text
golang.org/x/tools/gopls v0.24.0
```

The install took 36 seconds. With `sbx ssh-config --install` done once on your laptop, we added a type error to a file and asked gopls about it over SSH:

```bash
ssh sb_qvmfxnvkgt.sbx 'bash -lc "cd cli && gopls check pkg/cmd/pr/create/create.go"'
```

```text
/home/user/cli/pkg/cmd/pr/create/create.go:1404:28-31: cannot use "x" (untyped string constant) as int value in return statement
```

So gopls runs in the sandbox against its module and build caches. In VS Code or Cursor, connect with **Remote-SSH** to `sb_qvmfxnvkgt.sbx`, open `/home/user/cli` and install the Go extension on the remote side; it looks for gopls on that `PATH`. We tested `gopls check` over SSH, not the editor UI. Setup and troubleshooting for the editors are in [SSH into a cloud sandbox from VS Code or Cursor](/guides/ssh-into-a-cloud-sandbox-from-vs-code-or-cursor).

## 6. Save it as a named snapshot and start from it

```bash
sbx snapshot sb_qvmfxnvkgt go-cache
sbx new --name go-dev-2 --type small --from go-cache
```

The save took 2.1 seconds and the new sandbox was ready in 3.2 seconds. The snapshot holds the whole disk: Go 1.27.3 in `/usr/local/go`, the clone, both caches and gopls. In the new sandbox:

```text
go version go1.27.3 linux/amd64
/home/user/go/bin/gopls
== build, as saved
4.642 s
== go test ./..., as saved
3.121 s
exit 0
257
== go test -count=1 ./..., as saved
98.650 s
== build, module cache only (go clean -cache)
63.315 s
```

The first build took 4.6 seconds instead of 87. Go still hashed every input to confirm the cache was valid. 257 of 259 test packages came back `(cached)`. With only the module cache (after `go clean -cache`), the build took 63 seconds, the same as in the original sandbox. To name the snapshot by a hash of `go.sum` and rebuild it when dependencies change, follow [Cache dependencies in a named snapshot](/guides/cache-dependencies-in-a-named-snapshot).

## 7. Small or default for builds

We started a `default` sandbox from the same snapshot (`--type default`; a larger size than the snapshot works) and timed the compile with a warm module cache and an empty build cache:

```text
4
== build, module cache only, run 1
38.331 s
== build, module cache only, run 2
35.168 s
```

That is about 37 seconds on 4 vCPU against 63 on 2 vCPU: 1.7 times faster at twice the vCPUs. The Go compiler builds packages in parallel, so cold builds scale with cores, while a warm build is a few seconds on either size. How each size does on a plain CPU benchmark is in [CPU performance by sandbox size](/guides/cpu-performance-by-sandbox-size).

## FAQ

### Which Go version does a Sandboxer sandbox have?

On 2026-10-27, Go 1.27.2 from the official tarball in `/usr/local/go`. The image is updated over time, so check `go version`. With `GOTOOLCHAIN=auto`, a repo that pins a newer toolchain downloads it on first use.

### Where are GOCACHE and GOMODCACHE in a sandbox?

`GOCACHE` is `/home/user/.cache/go-build` and `GOMODCACHE` is `/home/user/go/pkg/mod`. Both are on the sandbox disk, so they survive `sbx stop` and are saved in a named snapshot or a fork.

### Do I need to snapshot the build cache, or only the modules?

Save both. For the GitHub CLI on `small`, modules alone took a build from 87 to 63 seconds; with the build cache it took 4.6 seconds. Together they used about 1.25 GB.

### Can I build binaries for macOS or Windows in a sandbox?

Yes, for pure Go code. Set `GOOS`, `GOARCH` and `CGO_ENABLED=0`, then copy the result out with `sbx scp`. Code that needs cgo also needs a C cross-compiler for the target.

### Should I use small or default for Go builds?

A cold build of the GitHub CLI took 63 seconds on `small` and about 37 on `default`. If you start from a snapshot with a warm build cache, `small` builds in seconds.

## Related

- [Set up a Python dev environment in a cloud sandbox](/guides/python-dev-environment-in-a-cloud-sandbox)
- [Cache dependencies in a named snapshot](/guides/cache-dependencies-in-a-named-snapshot)
- [CPU performance by sandbox size](/guides/cpu-performance-by-sandbox-size)
- [Set up a Rust dev environment in a cloud sandbox](/guides/rust-dev-environment-in-a-cloud-sandbox)
