Summary
A server configured to connect through a Network Agent must be given the destination's IP address. An internal hostname does not work, even when the machine running the agent can resolve that name itself.
This is surprising to users, who reasonably expect the agent — which sits inside their network and has access to their internal DNS — to perform the lookup.
Current behaviour
The destination hostname is resolved by DeployHQ before the tunnel connection is opened, and only the resulting IP address is sent to the agent. If the name exists only in the customer's internal DNS, that lookup fails and the deployment reports:
Could not resolve hostname
The agent is never asked to resolve anything. The CREATE_REQUEST payload is "<ip>/<port>" — see internal/protocol and the wire format documented in CLAUDE.md — so there is no field in which a name could be carried.
Separately, the access list is IP/CIDR-only, so a name would be rejected with "Destination address not allowed" even if one were forwarded:
- Go:
internal/acl/accesslist.go — net.ParseIP returns nil for a name, so Allows returns false
- Ruby (
deployhq/deploy-agent): lib/deploy_agent/server_connection.rb — IPAddr.new(network).include?(destination), which denies on IPAddr::InvalidAddressError
Workarounds available today
- Enter the destination's IP address (supported, and what the docs now recommend).
- Publish a public DNS record pointing at the internal IP — e.g. an A record for
server.example.com → 10.0.0.5. The name resolves publicly to the internal address, which is then handed to the agent, and the agent connects normally. The IP still needs to be covered by agent.access.
Proposed change
Let the agent resolve destination hostnames itself, using the DNS of the network it runs in.
Docs
The support article on Network Agents previously listed hostnames as valid agent.access entries and used server.local as an example. That has never worked — non-IP entries are silently skipped — and is likely what set the expectation here. Docs are being corrected separately to state that entries must be IPs or CIDR ranges, and to explain that the destination must be given as an IP address.
This repository's README.md was already correct ("Allowed destination IPs/CIDRs").
Summary
A server configured to connect through a Network Agent must be given the destination's IP address. An internal hostname does not work, even when the machine running the agent can resolve that name itself.
This is surprising to users, who reasonably expect the agent — which sits inside their network and has access to their internal DNS — to perform the lookup.
Current behaviour
The destination hostname is resolved by DeployHQ before the tunnel connection is opened, and only the resulting IP address is sent to the agent. If the name exists only in the customer's internal DNS, that lookup fails and the deployment reports:
The agent is never asked to resolve anything. The
CREATE_REQUESTpayload is"<ip>/<port>"— seeinternal/protocoland the wire format documented inCLAUDE.md— so there is no field in which a name could be carried.Separately, the access list is IP/CIDR-only, so a name would be rejected with "Destination address not allowed" even if one were forwarded:
internal/acl/accesslist.go—net.ParseIPreturnsnilfor a name, soAllowsreturnsfalsedeployhq/deploy-agent):lib/deploy_agent/server_connection.rb—IPAddr.new(network).include?(destination), which denies onIPAddr::InvalidAddressErrorWorkarounds available today
server.example.com→10.0.0.5. The name resolves publicly to the internal address, which is then handed to the agent, and the agent connects normally. The IP still needs to be covered byagent.access.Proposed change
Let the agent resolve destination hostnames itself, using the DNS of the network it runs in.
CREATE_REQUESTwithout breaking wire compatibility withdeploy-agentv1.4.1. The payload is already a variable-length string so a name may pass through as-is, but older Ruby agents would then reject it at the ACL check — some form of capability negotiation is likely needed.deploy-agent— decide whether to backport or gate the feature on the Go agent only.Docs
The support article on Network Agents previously listed hostnames as valid
agent.accessentries and usedserver.localas an example. That has never worked — non-IP entries are silently skipped — and is likely what set the expectation here. Docs are being corrected separately to state that entries must be IPs or CIDR ranges, and to explain that the destination must be given as an IP address.This repository's
README.mdwas already correct ("Allowed destination IPs/CIDRs").