Fixing 'listen tcp :8080: bind: address already in use' in Go

beginner🔷 Go2026-04-10| Go (Golang) on Linux, macOS, or Windows

Error Message

listen tcp :8080: bind: address already in use
#go#http#network#port#bind#server

The ProblemYou've just finished a feature, you're ready to test, and you hit go run main.go. Instead of a 'Server started' message, you're staring at a frustrating wall of text: listen tcp :8080: bind: address already in use. It's a rite of passage for backend developers. Essentially, your Go program is knocking on port 8080, but the operating system has already given the keys to someone else.

Why is your port being stubborn?It’s rarely a bug in your logic. Usually, one of these three culprits is to blame:

  • The Ghost Process: Your previous server crashed or you closed the terminal without stopping it. It’s still running invisibly in the background.- Port Squatters: A rogue Docker container, a forgotten Node.js instance, or a system service like Nginx is already using that port.- TCP TIME_WAIT: If you're restarting in a loop, the OS might hold the port in a TIME_WAIT state for 60 seconds to ensure all packets are cleared.## Step-by-Step Fix### 1. Track down the process (Linux or macOS)We need to find the Process ID (PID) of the squatter. Open your terminal and run:
lsof -i :8080

Your output will look like this:

COMMAND   PID   USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
main    45821   user    3u  IPv6 0x...      0t0  TCP *:http-alt (LISTEN)

Focus on the PID—in this example, it's 45821.

2. Evict the squatterNow, tell that process to pack its bags. Replace 45821 with your actual PID:

kill -9 45821

The -9 flag (SIGKILL) is the heavy hitter. It forces the process to stop immediately without asking questions. Now, try running your server again.

3. The Windows approachWindows handles things differently. Open PowerShell or Command Prompt as an Administrator and run:

netstat -ano | findstr :8080

Look for the line ending in a number:

TCP    0.0.0.0:8080           0.0.0.0:0              LISTENING       7244

That 7244 is your PID. Kill it with this command:

taskkill /F /PID 7244

Stop the cycle with Graceful ShutdownManual killing is a band-aid. If you see this error constantly, your Go code likely isn't cleaning up after itself when you hit Ctrl+C. By default, http.ListenAndServe just dies, often leaving the port hanging. Use a graceful shutdown to release resources properly.

Here is a battle-tested snippet to ensure your server exits cleanly:

package main

import (
	"context"
	"errors"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

func main() {
	srv := &http.Server{
		Addr: ":8080",
	}

	go func() {
		if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
			log.Fatalf("listen: %s\n", err)
		}
	}()

	// Listen for Ctrl+C or kill signals
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
	<-stop

	log.Println("Shutting down gracefully...")

	// Give the server 5 seconds to finish active requests
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if err := srv.Shutdown(ctx); err != nil {
		log.Fatal("Server forced to shutdown:", err)
	}

	log.Println("Server exited")
}

VerificationBefore you restart, double-check that the coast is clear. Run lsof -i :8080 again. If it returns nothing, you're good to go. You can also test with curl:

curl -I localhost:8080

Getting a 'Connection refused' error? Perfect. That means the port is wide open and waiting for your Go server.

Networking Pro-TipsSometimes the conflict isn't just a leftover process. If you're managing complex subnets in Docker or Kubernetes, debugging IP ranges can get messy. I frequently use the Subnet Calculator on ToolCraft to verify my CIDR masks and local IP ranges. It’s a handy way to ensure your service isn't trying to bind to an address that's already reserved by a virtual network interface.

One last trick: During local testing or CI/CD runs, use port :0. Go will assign a random, high-numbered port that's guaranteed to be free, saving you from 'address already in use' errors during automated tests.

Related Error Notes