{"record":{"id":"dafc7b22e9bf7871","repo":"basecamp/kamal","slug":"no-hosts-match-for-hosts-join","errorCode":null,"errorMessage":"No --hosts match for #{hosts.join(',')}","messagePattern":"No --hosts match for #(.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/kamal/commander.rb","lineNumber":68,"sourceCode":"    else\n      self.specific_hosts = [ config.primary_host ]\n    end\n  end\n\n  def specific_roles=(role_names)\n    @specifics = nil\n    @specific_roles = if role_names.present?\n      filtered = Kamal::Utils.filter_specific_items(role_names, config.roles)\n      raise ArgumentError, \"No --roles match for #{role_names.join(',')}\" if filtered.empty?\n      filtered\n    end\n  end\n\n  def specific_hosts=(hosts)\n    @specifics = nil\n    @specific_hosts = if hosts.present?\n      filtered = Kamal::Utils.filter_specific_items(hosts, config.all_hosts)\n      raise ArgumentError, \"No --hosts match for #{hosts.join(',')}\" if filtered.empty?\n      filtered\n    end\n  end\n\n  def with_specific_hosts(hosts)\n    original_hosts, self.specific_hosts = specific_hosts, hosts\n    yield\n  ensure\n    self.specific_hosts = original_hosts\n  end\n\n  def accessory_names\n    config.accessories&.collect(&:name) || []\n  end\n\n  def app(role: nil, host: nil)\n    Kamal::Commands::App.new(config, role: role, host: host)\n  end","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/basecamp/kamal/blob/eee0083b38661c3707c6b6052cc89e85038a096c/lib/kamal/commander.rb#L50-L86","documentation":"Kamal::Commander#specific_hosts= is called when you pass the global --hosts filter (e.g. `kamal deploy --hosts 1.2.3.4,5.6.7.8`). It filters config.all_hosts with Kamal::Utils.filter_specific_items (fnmatch with FNM_EXTGLOB, so wildcards/extended globs are allowed). If none of the configured hosts match any filter, the result is empty and Kamal raises ArgumentError ('No --hosts match for ...').","triggerScenarios":"`kamal deploy --hosts 10.0.0.9` when config lists only public hostnames; a glob matching no host (`--hosts 'db-*'`); typo'd or stale IPs after infrastructure changes; passing a role name instead of a hostname to --hosts; quoting that leaves brackets/commas attached to the filter string.","commonSituations":"Hosts changed in deploy.yml but runbooks/CI still pass old IPs; environments (destinations) with different host sets; SSH-config aliases that differ from the hosts in deploy.yml; copying a --roles example and using it for --hosts.","solutions":["Check the hosts defined in your config (`kamal app details` or read the servers: blocks in config/deploy*.yml) and pass exactly those names/IPs to --hosts.","If using a destination (-d), remember host lists come from deploy.<destination>.yml — verify you filtered with values from that file.","Use fnmatch-compatible patterns ('203.0.113.*') since regex alternation is not supported.","If you meant to select by role, use --roles <role-name> instead."],"exampleFix":"# before\n# config/deploy.yml servers web: hosts: 203.0.113.10, 203.0.113.11\nkamal deploy --hosts 10.0.0.10   # internal IP not in config\n# after\nkamal deploy --hosts 203.0.113.10\n# or pattern-match a subnet:\nkamal deploy --hosts '203.0.113.*'","handlingStrategy":"validation","validationCode":"require \"yaml\"\nconfig = YAML.load_file(\"config/deploy#{ENV[\"KAMAL_DESTINATION\"] ? \".#{ENV[\"KAMAL_DESTINATION\"]}\" : \"\"}.yml\")\nhosts = config[\"servers\"].values.flat_map { |r| Array(r[\"hosts\"]) }\nabort \"host not in config\" unless hosts.any? { |h| File.fnmatch(ARGV[0], h.to_s) }","typeGuard":"def hosts_match?(filters, configured_hosts)\n  filters.any? { |f| configured_hosts.any? { |h| File.fnmatch(f, h.to_s, File::FNM_EXTGLOB) } }\nend","tryCatchPattern":"begin\n  Kamal::Commander.new.specific_hosts = %w[203.0.113.10]\nrescue ArgumentError => e\n  raise unless e.message.include?(\"--hosts\")\n  abort \"check host names in deploy config: #{e.message}\"\nend","preventionTips":["Source --hosts values from the same deploy.yml (or IaC output) rather than hardcoding IPs in scripts.","When using destinations, validate against the destination overlay's host list, not the base file.","Prefer --roles for group targeting; use --hosts for single-host ops."],"tags":["kamal","hosts","cli","filter","config"],"backgroundTag":"no-matching-targets","analyzedSha":"eee0083b38661c3707c6b6052cc89e85038a096c","analyzedAt":"2026-08-21T15:17:22.045Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}