go run - Compile and Run Go Programs in One Step
Learn how go run compiles and executes Go source files in a single command, how it handles arguments, and when to use it instead of go build
go run compiles one or more Go source files and immediately executes the resulting binary — all in a single step. The binary is placed in a temporary directory and removed after execution, so no permanent executable remains on disk. Developers use it when they want a zero-ceremony way to test a program during development or to run small scripts without the overhead of a separate build step.
How go run Works Internally
When you type go run main.go, the go command does the following under the hood:
- It resolves the given
.gofiles and any packages they import. - It compiles them to a temporary executable, just as
go buildwould, but the output goes into a system temp folder (e.g.,/tmpon Linux,%TEMP%on Windows). - It runs that temporary binary with any arguments you passed after the file name.
- Once the program exits, Go cleans up the binary.
You can see the exact steps by using the -x flag, which prints every subcommand Go runs:
go run -x main.go
The output will show mkdir, compile, link commands pointing to temporary paths. The binary gets a random name like /tmp/go-build123456789/b001/exe/main and disappears afterward.
This behaviour explains two important practical consequences: running go run repeatedly will recompile each time (though Go’s build cache shortens subsequent compilations), and the executable never stays in your current directory.
Why go run Exists — the Fast Feedback Loop
Before Go 1.0, running a program required two explicit steps: go build then ./program. That workflow introduces friction when you are experimenting. go run was designed to collapse compilation and execution into one command, which is ideal when:
- You are prototyping a small utility and want to see output immediately.
- You are writing a quick script that does not deserve a permanent binary.
- You are teaching Go and want students to run code without explaining the build process first.
Think of go run as the Go equivalent of python my_script.py: you write code, you run it, you see the result. The difference is that Go still compiles to machine code under the hood, but you do not have to manage that compilation manually.
Basic Usage and Syntax
The simplest invocation is:
go run main.go
This compiles main.go (which must declare package main and contain a func main()) and runs it. If your program relies on other .go files in the same main package, list them all:
go run main.go helpers.go config.go
go run does not accept package import paths directly. You cannot write go run ./cmd/server — that will fail because go run expects .go files, not packages. (To run a package as if it were a command, use go run . from within that directory, which tells Go to compile the package in the current directory.)
Forgetting to pass all source files:
If your program is split across multiple files in the same directory and you only list one, compilation will fail with errors about undefined functions. Always include every .go file that belongs to the main package when using go run with explicit file arguments. Using go run . avoids this by compiling the whole package automatically.
Passing Arguments to the Running Program
Everything after the .go file name (or after . if you use go run .) is passed to the compiled binary, not to the go tool itself. For example:
go run main.go --port 8080 --verbose
Here --port 8080 --verbose become os.Args[1:] inside the program. The executable itself is os.Args[0], which will be the temporary path to the binary (something like /tmp/go-build...). For most programs this distinction does not matter, but if your code inspects os.Args[0] to derive a config path, you might get surprising results with go run.
A small program that prints its arguments makes this visible:
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println("Args:", os.Args)
}
Running it:
go run args.go hello world
The output includes the long temporary path followed by hello and world. The fact that the executable path is temporary is a key difference from running a pre-built binary where os.Args[0] is typically the command name the user typed.
Flag placement matters:
Flags meant for go run (like -x or -race) must appear before the .go file arguments. Anything after the first file argument is treated as a program argument. For example, go run -race main.go enables the race detector, but go run main.go -race passes the string -race to your program.
Useful Flags for Debugging and Inspection
go run accepts most of the same build flags as go build. The ones most useful during the run-edit loop are:
-n— Print the commands that would be executed without actually running them. Useful for understanding whatgo runplans to do.-x— Print each subcommand as it executes. Shows the exact compile and link invocations, along with their temporary directories.-v— Print the names of packages as they are compiled. Helps when you want to see what is being rebuilt.-work— Print the path to the temporary work directory and do not delete it after the run finishes. This lets you inspect the compiled binary and other artifacts.-race— Build with the data race detector enabled. Essential for concurrency-heavy code.
For example, to see exactly what happens during a run and keep the temporary build artifacts:
go run -x -work main.go
The output will contain a line like WORK=/tmp/go-build123456789; you can navigate there after the program exits and see the binary.
-work is a diagnostic superpower:
When you need to inspect the exact binary that ran or check whether a specific dependency was included, -work gives you access to a snapshot of the build that you would otherwise lose. It’s one of the first flags to reach for when go run produces unexpected behaviour.
Working with Multiple Files and Modules
When your code is organised into a Go module (with a go.mod file), go run respects the module context automatically. You can either run from the module root with go run . to compile and execute the main package in the current directory, or list specific .go files as before. The module’s dependencies are resolved from go.mod and go.sum; you do not need to run go get before go run — the go command will fetch missing dependencies automatically.
If your project uses a package layout (say cmd/server/main.go imports other packages from the same module), run from the cmd/server directory:
cd cmd/server
go run .
This compiles the main package in cmd/server along with all its local imports. The compilation uses the module context from the nearest go.mod upward in the directory tree.
Running from a directory without a main package:
Using go run . inside a directory that does not contain a main package causes a build error: go run: cannot run non-main package. The go run command requires a runnable program, so always point it at a main package.
When Not to Use go run
go run is intentionally a development convenience, not a production deployment tool. Avoid it when:
- You need consistent startup time. Each
go runpays the cost of compilation, even if the build cache reduces it. A pre-built binary starts instantly. - You want to distribute a single binary.
go runleaves no permanent artifact. You needgo buildorgo installto produce an executable that you can copy to other machines. - You are deploying to a server or container. Production environments should run a compiled binary — it is smaller, starts faster, and does not require the Go toolchain to be installed.
- You are running the same program repeatedly in a script or CI pipeline. While
go runis acceptable for one-off scripts in CI, a workflow that runs the same program many times benefits from a separatego buildstep followed by executing the binary.
A useful mental model: go run is a REPL shortcut, not a build system. Use it while you are editing code; switch to go build or go install once the code is stable.
Common Mistakes and Misconceptions
Mistaking go run for a script interpreter. Newcomers sometimes think go run interprets Go source line-by-line like Python or Node.js. It does not — it compiles the entire program to machine code first, then runs it. That means compile errors must be fixed before anything executes, unlike a dynamic language.
Forgetting that arguments after the file go to the program, not to go. A user might write go run main.go -v expecting verbose output from go run. In reality, -v becomes a program argument. Build flags must appear before the .go file.
Expecting go run to cache the binary across runs. Each invocation creates a fresh temporary binary. The Go build cache does cache compiled package objects, which speeds up recompilation, but the final executable is still rebuilt each time. This makes repeated runs slower than running a pre-built binary.
Ignoring the temporary os.Args[0]. Code that behaves differently based on the executable’s path may break under go run because os.Args[0] becomes a long, random-looking temp path instead of a clean command name. If your program uses os.Args[0] to find configuration files relative to the binary, use go build and run the resulting executable instead.
Trying to go run a remote package. You cannot write go run github.com/user/project because go run works only with local .go files or a local package (.). To compile and run a remote tool in one step, use go install github.com/user/project@latest && $(which project) or similar.
go run in Development vs Production
During development, go run is the quickest path from editor to running program. Combine it with a file watcher or a terminal split to get near-instant feedback. For larger projects, developers often keep a persistent binary built with go build and restart it manually, but for a single-file experiment or a microservice during initial prototyping, go run eliminates the build/run toggle entirely.
In production, never use go run. A production container should contain only the statically linked binary — not the Go toolchain, not the source code. The binary produced by go build is self-sufficient; go run adds unnecessary dependencies and startup latency.
go run in CI pipelines:
Using go run in a CI script is acceptable for light tasks that run once per pipeline (e.g., a code generator). But if you are running the same tool many times, build it once with go build and reuse the binary to avoid paying the compile cost repeatedly.
Summary
go run compiles and executes Go programs in one command by placing a temporary binary in a system temp directory. It shines in development when you need to see results without an explicit build step. The mental model is simple: it is a compile-then-run shortcut, not an interpreter, and it leaves no artifact behind.
As soon as you need to ship a binary, measure exact startup time, or run the same program repeatedly, go build or go install becomes the correct tool. Understanding the temporary nature of go run — and its implications for os.Args[0], flag placement, and file arguments — prevents the most common frustrations developers encounter with this command.