basecamp/kamal · error · Kamal::ConfigurationError
No servers specified for the #{role.name} role. You can igno
Error message
No servers specified for the #{role.name} role. You can ignore this with allow_empty_roles: true What it means
After validating the primary role, Kamal iterates every role and requires each non-primary role to have at least one host, unless `allow_empty_roles: true` is set. This Kamal::ConfigurationError fires for the first role whose hosts array is empty, with the role name in the message and a pointer to the escape hatch. The check exists because silently skipping an empty role usually masks a templating mistake.
Source
Thrown at lib/kamal/configuration.rb:354
end
raise Kamal::ConfigurationError, "Missing required configuration for image" if image.blank?
if raw_config.servers.nil?
raise Kamal::ConfigurationError, "No servers or accessories specified" unless raw_config.accessories.present?
else
unless role(primary_role_name).present?
raise Kamal::ConfigurationError, "The primary_role #{primary_role_name} isn't defined"
end
if primary_role.hosts.empty?
raise Kamal::ConfigurationError, "No servers specified for the #{primary_role.name} primary_role"
end
unless allow_empty_roles?
roles.each do |role|
if role.hosts.empty?
raise Kamal::ConfigurationError, "No servers specified for the #{role.name} role. You can ignore this with allow_empty_roles: true"
end
end
end
end
true
end
def ensure_valid_service_name
raise Kamal::ConfigurationError, "Service name can only include alphanumeric characters, hyphens, and underscores" unless raw_config[:service] =~ /^[a-z0-9_-]+$/i
true
end
def ensure_valid_kamal_version
if minimum_version && Gem::Version.new(minimum_version) > Gem::Version.new(Kamal::VERSION)
raise Kamal::ConfigurationError, "Current version is #{Kamal::VERSION}, minimum required is #{minimum_version}"
endView on GitHub (pinned to eee0083b38)
Solutions
- Populate the empty role's hosts list if the role should really deploy.
- If the role is legitimately empty in this environment, add top-level `allow_empty_roles: true` (or set it per environment).
- Remove the role key entirely if you no longer need it — cleaner than keeping an empty entry.
- Make ERB host lists raise on missing input rather than rendering [].
Example fix
# config/deploy.yml — before
servers:
web:
- 1.2.3.4
workers:
hosts: []
# after
allow_empty_roles: true
servers:
web:
- 1.2.3.4
workers:
hosts: [] Defensive patterns
Strategy: validation
Validate before calling
def roles_have_hosts_unless_allowed?(path = "config/deploy.yml")
raw = YAML.safe_load(ERB.new(File.read(path)).result, aliases: true) || {}
return true if raw["allow_empty_roles"]
primary = raw["primary_role"] || "web"
raw["servers"].to_a.all? do |name, entry|
hosts = entry.is_a?(Hash) ? entry["hosts"] : entry
name == primary || Array(hosts).any?
end
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
- Set `allow_empty_roles: true` only in environment overlays that genuinely lack optional roles.
- Delete role keys you no longer use rather than emptying them.
- Make ERB host arrays raise on missing input instead of emitting [].
When it happens
Trigger: `servers: { web: [...], workers: { hosts: [] } }` with no `allow_empty_roles: true`; ERB-generated role hosts (e.g. sidekiq hosts from an env var) that render empty in one environment; a role temporarily emptied while migrating infrastructure.
Common situations: Multi-environment configs where workers exist in production but not staging; host lists driven by ENV that are unset locally; role pruning during scale-down that leaves the key behind.
Related errors
- No servers specified for the #{primary_role.name} primary_ro
- No servers or accessories specified
- The primary_role #{primary_role_name} isn't defined
- Different roles can't share the same host for SSL: #{duplica
- Conflicting proxy run configurations for host #{host}
AI-assisted analysis of basecamp/kamal@eee0083b38 (2026-08-21).
Data as JSON: /api/errors/257d077b8b548af6.
Report an issue: GitHub.