ipify

ipify resolves the instance public IP through the ipify service, with URL validation at construction and a configurable fallback value on failure.

Part of nurago, a collection of independent Go packages for backend services.

import "github.com/tecnickcom/nurago/pkg/ipify"

Asks ipify.org what address the outside world sees this process as having.

c, err := ipify.New()
ip, err := c.GetPublicIP(ctx)

Defaults: endpoint https://api.ipify.org, timeout 4 seconds.

Why a process cannot answer this itself

A service behind NAT, in a container, on a cloud instance, or behind a load balancer has local interface addresses that bear no relation to the address its outbound traffic appears to come from. Reading net.Interfaces() gives you 10.0.x.x or 172.17.x.x, which is not the answer to “what should I tell a partner to allowlist”.

The only way to learn the public address is to ask something outside the network. That makes this package a network call with all a network call’s failure modes, rather than a lookup.

It appears in service status endpoints, where operators want the egress address visible next to the other diagnostics, and in startup logs for allowlist-based integrations.

Construction validates, calls fall back

New rejects a URL that is missing, unparseable, or not http/https with a host. A non-positive timeout is clamped to the default, rather than meaning “no timeout” as a bare zero would to http.Client.

Catching a bad endpoint at construction means a misconfiguration surfaces at startup rather than the first time the status endpoint is hit.

At request time the behaviour is the opposite. When request creation, transport, status-code validation, or body reading fails, GetPublicIP returns the configured error IP alongside the error rather than an empty string. WithErrorIP sets that value.

Returning a usable sentinel and an error lets a status handler render something rather than nothing, while a caller that checks the error still sees the failure. Code that ignores the error gets a marker value it can recognise instead of an empty field.

Options

OptionEffect
WithURLa different endpoint, validated
WithTimeoutrequest timeout, non-positive clamped to 4s
WithHTTPClientinject a transport or a mock
WithErrorIPthe value returned alongside an error

WithURL matters more than it looks. ipify is a free third-party service with no availability commitment, and it is a dependency in your request path if you call it on demand. Several compatible endpoints exist, and WithURL points the client at one you control.

HealthCheck

Client.HealthCheck probes endpoint reachability, matching the method every other nurago client exposes so it can be registered with healthcheck alongside the rest.

Consider whether a third-party lookup belongs in a readiness probe: a service that is otherwise healthy will be marked unready when ipify has an outage.

Caching

The public address of a running instance changes rarely. GetPublicIP performs a request every call and does no caching of its own.

Resolve once at startup, or wrap the call in sfcache with a long TTL, which also collapses concurrent lookups into one request.

Example

c, err := ipify.New(ipify.WithTimeout(2 * time.Second))
if err != nil {
    log.Fatal(err)
}

ip, err := c.GetPublicIP(context.Background())

fmt.Println(ip, err)

Full source is in example_ipify_test.go. More runnable examples are on pkg.go.dev.

Dependencies

This package reaches no external module: it uses only the Go standard library.