basecamp/kamal · error · Kamal::ConfigurationError
Local registry with remote builder requires an SSH URL (e.g.
Error message
Local registry with remote builder requires an SSH URL (e.g., ssh://user@host)
What it means
Kamal::Configuration#ensure_local_registry_remote_builder_has_ssh_url fires when the registry is local (registry configured with no server) while the builder is remote, and the remote builder URL's URI scheme is not ssh. A local registry is only reachable from machines on that network, so a remote builder must connect over SSH (e.g. ssh://user@host) for the push/pull to work; http/tcp URLs would point the builder at a registry it cannot reach or secure.
Source
Thrown at lib/kamal/configuration.rb:411
def ensure_one_host_for_ssl_roles
roles.each(&:ensure_one_host_for_ssl)
true
end
def ensure_unique_hosts_for_ssl_roles
hosts = roles.select(&:ssl?).flat_map { |role| role.proxy.hosts }
duplicates = hosts.tally.filter_map { |host, count| host if count > 1 }
raise Kamal::ConfigurationError, "Different roles can't share the same host for SSL: #{duplicates.join(", ")}" if duplicates.any?
true
end
def ensure_local_registry_remote_builder_has_ssh_url
if registry.local? && builder.remote?
unless URI(builder.remote).scheme == "ssh"
raise Kamal::ConfigurationError, "Local registry with remote builder requires an SSH URL (e.g., ssh://user@host)"
end
end
true
end
def ensure_no_conflicting_proxy_runs
all_hosts.each do |host|
run_configs = proxy_runs(host)
if run_configs.uniq.size > 1
raise Kamal::ConfigurationError, "Conflicting proxy run configurations for host #{host}"
end
end
end
def proxy_runs(host)
(host_roles(host) + host_accessories(host)).map(&:proxy).compact.map(&:run).compact
endView on GitHub (pinned to eee0083b38)
Solutions
- Change the builder remote to an SSH URL: `builder: { remote: ssh://user@buildhost }`.
- Ensure the deploying user has SSH key access to the build host (test with `ssh user@buildhost docker version`).
- If the builder need not be remote, remove the `remote:` key so building happens locally against the local registry.
Example fix
# config/deploy.yml — before registry: username: deploy builder: remote: buildhost.example.com # after registry: username: deploy builder: remote: ssh://deploy@buildhost.example.com
Defensive patterns
Strategy: validation
Validate before calling
require "uri"
def local_registry_remote_builder_ok?(raw)
registry_local = raw.dig("registry", "server").nil?
remote = raw.dig("builder", "remote")
return true unless registry_local && remote
uri = URI.parse(remote.to_s)
uri.scheme == "ssh" || uri.scheme.nil? && remote.include?("@") ? uri.scheme == "ssh" : false
rescue URI::InvalidURIError
false
end Try / catch
begin
config = Kamal::Configuration.new(create_config_files: false)
rescue Kamal::ConfigurationError => e
puts "Deploy config invalid: #{e.message}"
exit 1
end Prevention
- Always write remote builder URLs with an explicit scheme (ssh://user@host).
- Test SSH access to the build host before wiring it into config.
- Keep local-registry setups paired only with ssh:// remotes or local builders.
When it happens
Trigger: A `registry:` block with no server (local registry) plus `builder: { remote: tcp://... }` or a plain hostname like `remote: buildhost.example.com` that parses to a nil scheme; switching registry to a local one but leaving an old docker-context-style remote builder URL; following a local-registry tutorial and pasting a bare hostname.
Common situations: Self-hosted registry on the deploy host with a remote build machine; migrating from docker context strings (which lack ssh://) to kamal 2 builder config; typos like `ssh//user@host` that parse to a nil scheme.
Related errors
- Invalid builder configuration: the `docker` driver does not
- Invalid builder configuration: the `docker` driver does not
- Invalid builder configuration: the `docker` driver does not
- Invalid cache type: #{@options["cache"]["type"]}
- You must specify a destination
AI-assisted analysis of basecamp/kamal@eee0083b38 (2026-08-21).
Data as JSON: /api/errors/94f1d02f17b9714e.
Report an issue: GitHub.