{"id":49920704,"url":"https://github.com/davafons/kamal-lint","last_synced_at":"2026-05-16T19:42:50.386Z","repository":{"id":357132117,"uuid":"1235532838","full_name":"davafons/kamal-lint","owner":"davafons","description":"Static linter for Kamal config/deploy.yml","archived":false,"fork":false,"pushed_at":"2026-05-11T14:18:31.000Z","size":106,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-11T14:27:23.299Z","etag":null,"topics":["deploy","devops","github-action","kamal","linter","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davafons.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"MIT-LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-11T12:17:27.000Z","updated_at":"2026-05-11T14:25:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/davafons/kamal-lint","commit_stats":null,"previous_names":["davafons/kamal-lint"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/davafons/kamal-lint","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davafons%2Fkamal-lint","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davafons%2Fkamal-lint/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davafons%2Fkamal-lint/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davafons%2Fkamal-lint/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davafons","download_url":"https://codeload.github.com/davafons/kamal-lint/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davafons%2Fkamal-lint/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33117015,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-16T18:38:32.183Z","status":"ssl_error","status_checked_at":"2026-05-16T18:38:29.903Z","response_time":115,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deploy","devops","github-action","kamal","linter","ruby"],"created_at":"2026-05-16T19:42:49.505Z","updated_at":"2026-05-16T19:42:50.381Z","avatar_url":"https://github.com/davafons.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003ekamal-lint\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://rubygems.org/gems/kamal-lint\"\u003e\u003cimg src=\"https://img.shields.io/gem/v/kamal-lint\" alt=\"Gem Version\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/davafons/kamal-lint/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/davafons/kamal-lint/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/davafons/kamal-lint/blob/main/MIT-LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/davafons/kamal-lint\" alt=\"License\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://rubygems.org/gems/kamal-lint\"\u003e\u003cimg src=\"https://img.shields.io/gem/dt/kamal-lint\" alt=\"Downloads\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nStatic linter for [Kamal](https://kamal-deploy.org) `config/deploy.yml`. Catches missing secrets, role/registry mismatches, and proxy footguns that Kamal silently allows.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/preview.svg\" alt=\"kamal-lint output sample\" width=\"760\"\u003e\n\u003c/p\u003e\n\n## Install\n\n```ruby\n# Gemfile\ngroup :development, :test do\n  gem \"kamal-lint\", require: false\nend\n```\n\n```bash\nbundle exec kamal-lint\n```\n\n## Usage\n\n**Default: lint base + every destination override at once.**\n\n```bash\nbundle exec kamal-lint\n```\n\nAuto-discovers `config/deploy.*.yml` files next to your base `config/deploy.yml` and lints each (base alone, then each destination merged onto base). Output groups findings by destination — bugs that live in a `deploy.production.yml` override show up *only* under `[production]`:\n\n```\n[base]       config/deploy.yml\n  ✓ No issues found.\n\n[production] config/deploy.production.yml\n  ⚠ warning  ...\n      `traefik:` block is Kamal 1.x legacy ...\n\n[staging]    config/deploy.staging.yml\n  ✓ No issues found.\n\nSummary: 1 warning across 3 configs\n```\n\nExits `1` if any findings are at or above `--fail-on` (default: `warning`), `0` otherwise.\n\n**Narrow to a single destination:**\n\n```bash\nbundle exec kamal-lint -d production\n```\n\nUseful in CI matrix jobs or when debugging one destination. Skips auto-discovery; only lints `deploy.yml + deploy.production.yml`.\n\n**Other knobs:**\n\n```bash\nbundle exec kamal-lint -c infra/deploy.yml      # non-default config path\nbundle exec kamal-lint list-checks              # show every registered check\nbundle exec kamal-lint --format=json            # machine-readable output\nbundle exec kamal-lint --include-kamal-errors   # also surface Kamal's loader errors\n```\n\n**In CI:**\n\n```yaml\n- uses: davafons/kamal-lint@v0.1.0\n  with:\n    fail-on: warning\n```\n\n`--format=github` is set automatically so findings show as inline annotations in the PR view. By default the action lints all destinations; set `destination: production` to narrow.\n\n## Checks\n\n| ID | Severity | What it catches |\n|---|---|---|\n| `secret-not-declared` | error | `env.secret` (top-level or per-accessory) references a key that isn't declared in `.kamal/secrets`. Kamal would fail at deploy time. |\n| `accessory-role-undefined` | error | An accessory's `roles:` lists a role name that isn't defined under `servers:`. The accessory won't deploy to anything. |\n| `role-hosts-empty` | error | A role under `servers:` has no hosts. Deploys to that role silently no-op. |\n| `image-registry-mismatch` | error | `image:` doesn't include the prefix of `registry.server`. Kamal would push/pull from the wrong registry. (Docker Hub is exempt — unprefixed images resolve there automatically.) |\n| `builder-registry-secret-undeclared` | error | `registry.username` or `registry.password` references a secret name that isn't in `.kamal/secrets`. |\n| `ssl-without-host` | error | `proxy.ssl: true` without a `host:` (or `hosts:`). Let's Encrypt provisioning has nothing to issue against. |\n| `empty-web-role` | error | `servers:` is empty or every role has no hosts. Nothing would be deployed. |\n| `accessory-placement-missing` | error | An accessory has none of `host`, `hosts`, or `roles` declared, so Kamal has no idea where to put it. |\n| `missing-service-name` | error | `service:` is not set. Kamal can't name the container. |\n| `traefik-legacy-keys` | warning | A `traefik:` block is still present. Kamal 2+ uses `proxy:` and silently ignores the old block. |\n| `boot-limit-exceeds-hosts` | warning | `boot.limit` is greater than the total number of hosts, so the rolling-deploy limit has no effect. |\n| `kamal-secrets-not-gitignored` | warning | `.kamal/secrets` exists in the repo but isn't matched by `.gitignore`. Real credentials are one `git add .` away from a commit. |\n| `secret-in-env-clear` | warning | A key in `env.clear` looks like a secret (`*_KEY`, `*_TOKEN`, `*_SECRET`, `*PASSWORD*`). Move it to `env.secret` + `.kamal/secrets`. |\n| `missing-proxy-healthcheck` | warning | The `proxy:` block has no `healthcheck:`. Kamal-proxy can't verify a new release before cutting traffic — zero-downtime deploys may fail. |\n| `accessory-image-latest` | warning | An accessory's `image:` is pinned to `:latest` (or has no tag). Updates can change unexpectedly between deploys. |\n| `registry-without-explicit-server` | warning | `registry` is set but `registry.server` isn't. Kamal silently defaults to Docker Hub. |\n| `kamal-parse-error` | error | *Opt-in.* Surfaces errors from Kamal's own loader. Enable with `--include-kamal-errors`. Useful in CI as a complement to `kamal config`. |\n\nReasoning behind each finding is also in the message text — paste a finding into search and you'll usually land on the relevant Kamal doc.\n\n## Flags\n\n```\n-c, --config-file PATH      config/deploy.yml\n-d, --destination NAME      lint deploy.\u003cname\u003e.yml merged onto base\n-f, --format FORMAT         human | json | github\n    --fail-on LEVEL         error | warning | info\n    --kamal-version VER     override detected Kamal version\n    --include-kamal-errors  also surface Kamal's loader errors\n```\n\nExit codes: `0` clean · `1` findings at/above `--fail-on` · `2` config missing.\n\n## Kamal versions\n\n| kamal-lint | kamal | tested against |\n|---|---|---|\n| `0.1.x` | `\u003e= 2.0`, `\u003c 3.0` | latest 2.x |\n\n`kamal-lint` reuses your installed Kamal's loader, so it auto-tracks whatever's in your `Gemfile.lock`. Older 2.x versions likely work but aren't covered by CI — if you hit a bug on an older Kamal, please [open an issue](https://github.com/davafons/kamal-lint/issues). Override the detected version with `--kamal-version 2.5.0` for matrix runs.\n\n## Development\n\n```bash\nbin/setup     # install\nbin/test      # run tests\nbin/console   # IRB with kamal-lint loaded\n```\n\nContributions: [CONTRIBUTING.md](./CONTRIBUTING.md) · Security: [SECURITY.md](./SECURITY.md) · License: [MIT](./MIT-LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavafons%2Fkamal-lint","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavafons%2Fkamal-lint","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavafons%2Fkamal-lint/lists"}