basecamp/kamal · error · Kamal::ConfigurationError

The primary_role #{primary_role_name} isn't defined

Error message

The primary_role #{primary_role_name} isn't defined

What it means

Kamal::Configuration#ensure_required_keys_present requires that the role named by `primary_role` actually exists among the keys of the `servers:` hash. The primary role (default "web", overridable via the top-level `primary_role:` key) is the role Kamal runs migrations, boot checks, and traffic switching against. If servers are defined but none matches the primary role name, configuration loading fails with the offending name interpolated into the message.

Source

Thrown at lib/kamal/configuration.rb:344

      if require_destination? && destination.nil?
        raise ArgumentError, "You must specify a destination"
      end

      true
    end

    def ensure_required_keys_present
      %i[ service registry ].each do |key|
        raise Kamal::ConfigurationError, "Missing required configuration for #{key}" unless raw_config[key].present?
      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

View on GitHub (pinned to eee0083b38)

Solutions

  1. Add a role matching the primary role name under `servers:` (e.g. a `web:` entry with at least one host).
  2. Or set `primary_role: <existing-role-name>` at the top level so it points at a role you actually defined.
  3. Double-check spelling and underscores — the match is exact against the servers hash keys.
  4. If ERB computes the role name, inspect the rendered config (`kamal config primary_role`) to see the resolved value.

Example fix

# config/deploy.yml — before
primary_role: app
servers:
  web:
    hosts:
      - 1.2.3.4

# after
primary_role: app
servers:
  app:
    hosts:
      - 1.2.3.4
Defensive patterns

Strategy: validation

Validate before calling

def primary_role_defined?(path = "config/deploy.yml")
  raw = YAML.safe_load(ERB.new(File.read(path)).result, aliases: true) || {}
  primary = raw["primary_role"] || "web"
  raw["servers"].is_a?(Hash) && raw["servers"].key?(primary)
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

When it happens

Trigger: Setting `primary_role: app` while `servers:` only defines roles like `web:` or `workers:`; renaming the `web` role to something else without updating `primary_role`; using a primary_role name from ERB/ENV that resolves with a typo or different capitalization.

Common situations: Renaming roles during a restructure and forgetting the cross-reference; copying a config that uses a custom primary_role name into a project whose servers hash still uses default naming; deleting the web role intending an accessory-only or worker-only setup.

Related errors


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