nektos/act · error

unable to determine outbound IP address

Error message

unable to determine outbound IP address

What it means

When the artifact cache server starts, it needs an outbound IP to advertise in artifact/cache download URLs served to containers. If no explicit outbound IP was configured and common.GetOutboundIP() (which dials an external address, typically 8.8.8.8, to learn the default-route source address) returns nil — no route to the internet — the handler constructor fails with 'unable to determine outbound IP address'.

Source

Thrown at pkg/artifactcache/handler.go:85

		return nil, err
	}

	h.dir = dir

	storage, err := NewStorage(filepath.Join(dir, "cache"))
	if err != nil {
		return nil, err
	}
	h.storage = storage

	if customExternalURL != "" {
		h.customExternalURL = customExternalURL
	}

	if outboundIP != "" {
		h.outboundIP = outboundIP
	} else if ip := common.GetOutboundIP(); ip == nil {
		return nil, fmt.Errorf("unable to determine outbound IP address")
	} else {
		h.outboundIP = ip.String()
	}

	tokenBytes := make([]byte, 16)
	if _, err := rand.Read(tokenBytes); err != nil {
		return nil, fmt.Errorf("generate auth token: %w", err)
	}
	h.token = hex.EncodeToString(tokenBytes)

	router := httprouter.New()
	base := "/" + h.token + apiPath
	router.GET(base+"/cache", h.middleware(h.find))
	router.POST(base+"/caches", h.middleware(h.reserve))
	router.PATCH(base+"/caches/:id", h.middleware(h.upload))
	router.POST(base+"/caches/:id", h.middleware(h.commit))
	router.GET(base+"/artifacts/:id", h.middleware(h.get))
	router.POST(base+"/clean", h.middleware(h.clean))

View on GitHub (pinned to 4f41128141)

Solutions

  1. Pass an explicit outbound IP so no probe is needed: 'act --artifact-server-ip <host-ip>' (or set the corresponding Input field when using act as a library).
  2. Restore basic outbound connectivity: default route present and the probe address reachable (check 'ip route' / routing table).
  3. In locked-down networks, allow the host to dial the external probe address once, or always pin the IP as above.
  4. As a workaround for fully offline runs, disable artifact/cache features if your workflows do not use them.

Example fix

# before
act -j build
# -> unable to determine outbound IP address (no internet route)

# after
act --artifact-server-ip 192.168.1.20 -j build
Defensive patterns

Strategy: validation

Validate before calling

package main

import (
	"fmt"
	"net"
)

func outboundIPOrExplicit(explicit string) (string, error) {
	if explicit != "" {
		return explicit, nil
	}
	conn, err := net.Dial("udp", "8.8.8.8:80")
	if err != nil {
		return "", fmt.Errorf("no outbound route; pass --artifact-server-ip explicitly")
	}
	defer conn.Close()
	return conn.LocalAddr().(*net.UDPAddr).IP.String(), nil
}

Try / catch

handler, err := artifactcache.NewHandler(dir, externalURL, outboundIP, "")
if err != nil {
    if strings.Contains(err.Error(), "unable to determine outbound IP address") {
        // offline host: bind to an explicit local address and retry
        handler, err = artifactcache.NewHandler(dir, externalURL, mustDefaultOutboundIP(), "")
    }
    if err != nil {
        return err
    }
}

Prevention

When it happens

Trigger: Running act (which starts the artifact/cache server automatically for every job) on a host with no external network route: air-gapped machines, firewalls blocking UDP/TCP to the probe address, containers or VMs without a default gateway.

Common situations: Corporate/air-gapped environments where the dial-out probe silently fails; CI runners in isolated VLANs; act running inside a container itself without outbound access; misconfigured DNS/routing after VPN changes.

Related errors


AI-assisted analysis of nektos/act@4f41128141 (2026-08-15). Data as JSON: /api/errors/34c41280e1b565db. Report an issue: GitHub.