basecamp/kamal · error · ArgumentError
Invalid publish IP address: #{ip}
Error message
Invalid publish IP address: #{ip} What it means
Kamal::Configuration::Proxy::Boot#ensure_valid_bind_ips validates each publish/bind IP extracted from the proxy configuration against Resolv::IPv4::Regex and Resolv::IPv6::Regex. Any string that is not a syntactically valid IPv4 or IPv6 literal (hostnames, ranges, malformed addresses) raises this ArgumentError with the offending value, because Docker's --publish flag requires a literal bind address.
Source
Thrown at lib/kamal/configuration/proxy/boot.rb:110
end
def error_pages_container_directory
File.join app_container_directory, "error_pages"
end
def tls_directory
File.join app_directory, "tls"
end
def tls_container_directory
File.join app_container_directory, "tls"
end
private
def ensure_valid_bind_ips(bind_ips)
bind_ips.present? && bind_ips.each do |ip|
next if ip =~ Resolv::IPv4::Regex || ip =~ Resolv::IPv6::Regex
raise ArgumentError, "Invalid publish IP address: #{ip}"
end
true
end
def format_bind_ip(ip)
# Ensure IPv6 address inside square brackets - e.g. [::1]
if ip =~ Resolv::IPv6::Regex && ip !~ /\A\[.*\]\z/
"[#{ip}]"
else
ip
end
end
end
View on GitHub (pinned to eee0083b38)
Solutions
- Replace the invalid value with a literal IPv4 or IPv6 address (e.g. `127.0.0.1`, `192.168.1.10`, `::1`).
- If you only meant to fix the port mapping, keep the format IP:HOST_PORT:CONTAINER_PORT and ensure the first segment is a bare valid IP.
- For hostnames, resolve them to IPs first (dig/host) — docker publish cannot bind a DNS name.
Example fix
# config/deploy.yml — before (accessory proxy publish)
accessories:
admin:
proxy:
host: 1.2.3.4
publish:
- "myhost.example.com:8080:80"
# after
accessories:
admin:
proxy:
host: 1.2.3.4
publish:
- "192.168.1.10:8080:80" Defensive patterns
Strategy: validation
Validate before calling
require "resolv"
def valid_publish_ips?(publish_specs)
Array(publish_specs).all? do |spec|
ip = spec.to_s.split(":").first
ip =~ Resolv::IPv4::Regex || ip =~ Resolv::IPv6::Regex
end
end Type guard
require "resolv"
def valid_bind_ip?(value)
value.is_a?(String) &&
(value =~ Resolv::IPv4::Regex || value =~ Resolv::IPv6::Regex)
end Try / catch
begin
Kamal::Configuration.new(create_config_files: false)
rescue ArgumentError => e
abort "#{e.message} — publish binds need literal IPv4/IPv6 addresses"
end Prevention
- Always bind literal IPs (127.0.0.1, private LAN IPs, ::1), never hostnames.
- Lint publish strings with Resolv before committing proxy config.
- For IPv6, write the canonical form and let kamal bracket it in docker args.
When it happens
Trigger: A proxy `publish:` spec with a hostname instead of an IP (e.g. `myhost.example.com:80:80`); a malformed IPv4 like `192.168.1` or `999.1.1.1`; passing a whole host:port:port string where only the bare IP was expected; an IPv6 entry with wrong abbreviation syntax in the bind position.
Common situations: Trying to bind a service to a domain name instead of an interface IP; typos in hand-written publish args; NAT/private-network configs where developers guess internal addresses; IPv6 entries with stray colons.
Related errors
- Failed to get endpoint for #{role} on #{host}, did the conta
- Failed to get endpoint for #{role} on #{host}, did the conta
- Docker is not installed locally
- Docker buildx plugin is not installed locally
- container not ready after #{KAMAL.config.deploy_timeout} sec
AI-assisted analysis of basecamp/kamal@eee0083b38 (2026-08-21).
Data as JSON: /api/errors/11750b8b1462521b.
Report an issue: GitHub.