{"record":{"id":"5238984c15fa81fa","repo":"basecamp/kamal","slug":"service-name-can-only-include-alphanumeric-charact","errorCode":null,"errorMessage":"Service name can only include alphanumeric characters, hyphens, and underscores","messagePattern":"Service name can only include alphanumeric characters, hyphens, and underscores","errorType":"validation","errorClass":"Kamal::ConfigurationError","httpStatus":null,"severity":"error","filePath":"lib/kamal/configuration.rb","lineNumber":364,"sourceCode":"\n        if primary_role.hosts.empty?\n          raise Kamal::ConfigurationError, \"No servers specified for the #{primary_role.name} primary_role\"\n        end\n\n        unless allow_empty_roles?\n          roles.each do |role|\n            if role.hosts.empty?\n              raise Kamal::ConfigurationError, \"No servers specified for the #{role.name} role. You can ignore this with allow_empty_roles: true\"\n            end\n          end\n        end\n      end\n\n      true\n    end\n\n    def ensure_valid_service_name\n      raise Kamal::ConfigurationError, \"Service name can only include alphanumeric characters, hyphens, and underscores\" unless raw_config[:service] =~ /^[a-z0-9_-]+$/i\n\n      true\n    end\n\n    def ensure_valid_kamal_version\n      if minimum_version && Gem::Version.new(minimum_version) > Gem::Version.new(Kamal::VERSION)\n        raise Kamal::ConfigurationError, \"Current version is #{Kamal::VERSION}, minimum required is #{minimum_version}\"\n      end\n\n      true\n    end\n\n    def ensure_retain_containers_valid\n      raise Kamal::ConfigurationError, \"Must retain at least 1 container\" if retain_containers < 1\n\n      true\n    end\n","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/basecamp/kamal/blob/eee0083b38661c3707c6b6052cc89e85038a096c/lib/kamal/configuration.rb#L346-L382","documentation":"Kamal::Configuration#ensure_valid_service_name enforces that the `service:` value matches /^[a-z0-9_-]+$/i — alphanumeric characters, hyphens, and underscores only. The service name is used to derive hostnames, container names, and Docker resource labels, so characters like dots, spaces, or slashes would break downstream Docker/Traefik commands. Any deviation aborts configuration loading.","triggerScenarios":"Setting `service: myapp.com` or `service: my app` in config/deploy.yml; deriving the service name from an ENV var or ERB expression containing a dot or slash (e.g. a git remote or repo path); copying the service value from a container registry path like `team/myapp`.","commonSituations":"Using the domain or repo path as the service name; ERB interpolating `ENV[\"APP_NAME\"]` that unexpectedly contains whitespace; renaming a service to match a FQDN for cosmetic reasons.","solutions":["Change `service:` to contain only letters, digits, hyphens, and underscores (e.g. `myapp` instead of `myapp.com`).","If the name comes from ERB/ENV, sanitize it in the template (`ENV[\"APP_NAME\"].to_s.gsub(/[^a-zA-Z0-9_-]/, \"-\")`) or fix the source value.","Note that changing the service name changes derived hostnames and labels — clean up old containers and volumes from the previous name after migrating."],"exampleFix":"# config/deploy.yml — before\nservice: myapp.example.com\n\n# after\nservice: myapp","handlingStrategy":"validation","validationCode":"VALID_SERVICE = /\\A[a-z0-9_-]+\\z/i\n\ndef valid_service_name?(name) = VALID_SERVICE.match?(name.to_s)","typeGuard":"def kamal_service_name?(value)\n  value.is_a?(String) && value.match?(%r{\\A[a-zA-Z0-9_-]+\\z})\nend","tryCatchPattern":"begin\n  config = Kamal::Configuration.new(create_config_files: false)\nrescue Kamal::ConfigurationError => e\n  puts \"Deploy config invalid: #{e.message}\"\n  exit 1\nend","preventionTips":["Never derive service from a domain or registry path; pick a short slug once.","Sanitize ENV-sourced names in ERB: `ENV[\"APP_NAME\"].to_s.gsub(/[^a-zA-Z0-9_-]/, \"-\")`.","Remember changing the service name changes derived hostnames and labels — plan cleanup."],"tags":["kamal","configuration","validation","naming"],"backgroundTag":"invalid-identifier-format","analyzedSha":"eee0083b38661c3707c6b6052cc89e85038a096c","analyzedAt":"2026-08-21T15:17:22.045Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}