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

  1. Replace the invalid value with a literal IPv4 or IPv6 address (e.g. `127.0.0.1`, `192.168.1.10`, `::1`).
  2. 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.
  3. 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

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


AI-assisted analysis of basecamp/kamal@eee0083b38 (2026-08-21). Data as JSON: /api/errors/11750b8b1462521b. Report an issue: GitHub.