Getting local address in Go

Awhile ago I had some tests that would spin up a server and then hit some “localhost:8080” endpoints and verify the responses. The tests worked great in my IDE, but when the tests were invoked as a step during a Docker build, they could not find the server. This post is about how I got my tests to run within a Docker container.

There are a variety of ways to get the local address in a Docker container, but many of the solutions I found online involved running one or more command line utilities and I wanted something that “just worked” in Go. After various kicking and swearing (and searching), this is what I came up with:


package main

import (
    "fmt"
    "net"
    "os"
)

func main() {
    fmt.Println(getLocalAddress())
}

func getLocalAddress() (string, error) {
    var address string
    _, err := os.Stat("/.dockerenv") // 1
    if err == nil {
        conn, err := net.Dial("udp", "8.8.8.8:80") // 2
        if err != nil {
            return address, err
        }
        defer conn.Close()
        address = conn.LocalAddr().(*net.UDPAddr).IP.String() // 3
    } else {
        address = "localhost" // 4
    }
    return address, nil
}

The above is a complete main.go which can be compiled and ran, but the most important piece is the getLocalAddress() function which returns the local address (or an error if something goes wrong, but I haven’t had problems). Here are some of the key points of the code:

  1. Checking if the “/.dockerenv” file exists is an easy way to check if the code is running in a Docker container.
  2. We create a net.Conn instance
  3. I first tried using
    address = conn.LocalAddr().String()
    to get the local IP address, but it included a port that I didn’t want. Instead of doing string manipulation, I found the returned LocalAddr was a pointer to a net.UDPAddr and so a simple casting provided easy access to the IP.
  4. If not running in Docker, “localhost” is good enough.

And that’s about it. When I have checked, the local IP address of the container has been in the 172.17.0.x range which I believe is a Docker default. Since doing this, there haven’t been any problems running the test, but I’m not sure if this is an industrial strength solution suitable for production environments.