{"id":19205695,"url":"https://github.com/fairwindsops/saffire","last_synced_at":"2026-01-12T00:03:33.645Z","repository":{"id":37959028,"uuid":"271882550","full_name":"FairwindsOps/saffire","owner":"FairwindsOps","description":"[alpha] Controller to override image sources in the event that an image cannot be pulled.","archived":false,"fork":false,"pushed_at":"2025-03-24T14:18:33.000Z","size":241,"stargazers_count":116,"open_issues_count":6,"forks_count":5,"subscribers_count":13,"default_branch":"main","last_synced_at":"2025-03-29T14:12:40.915Z","etag":null,"topics":["docker-registry","fairwinds-incubator","kubernetes"],"latest_commit_sha":null,"homepage":"https://fairwinds.com","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FairwindsOps.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-06-12T20:15:36.000Z","updated_at":"2025-02-11T16:51:26.000Z","dependencies_parsed_at":"2024-12-23T13:14:19.563Z","dependency_job_id":"0a3e697e-3270-4592-acc3-71992e3b1c54","html_url":"https://github.com/FairwindsOps/saffire","commit_stats":null,"previous_names":["fairwindsops/kuiper"],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FairwindsOps%2Fsaffire","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FairwindsOps%2Fsaffire/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FairwindsOps%2Fsaffire/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FairwindsOps%2Fsaffire/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FairwindsOps","download_url":"https://codeload.github.com/FairwindsOps/saffire/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247353749,"owners_count":20925329,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["docker-registry","fairwinds-incubator","kubernetes"],"created_at":"2024-11-09T13:13:39.739Z","updated_at":"2026-01-12T00:03:33.619Z","avatar_url":"https://github.com/FairwindsOps.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\" class=\"no-border\"\u003e\n    \u003cimg src=\"/img/saffire.png\" height=\"150\" alt=\"Saffire\" style=\"padding-bottom: 20px\" /\u003e\n    \u003cbr\u003e\n    \u003ca href=\"https://github.com/FairwindsOps/saffire/releases\"\u003e\n        \u003cimg src=\"https://img.shields.io/github/v/release/FairwindsOps/saffire\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://goreportcard.com/report/github.com/FairwindsOps/saffire\"\u003e\n        \u003cimg src=\"https://goreportcard.com/badge/github.com/FairwindsOps/saffire\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://insights.fairwinds.com/gh/FairwindsOps/saffire\"\u003e\n      \u003cimg src=\"https://insights.fairwinds.com/v0/gh/FairwindsOps/polaris/badge.svg\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://join.slack.com/t/fairwindscommunity/shared_invite/zt-e3c6vj4l-3lIH6dvKqzWII5fSSFDi1g\"\u003e\n      \u003cimg src=\"https://img.shields.io/static/v1?label=Slack\u0026message=Join+our+Community\u0026color=4a154b\u0026logo=slack\"\u003e\n    \u003c/a\u003e\n\u003c/div\u003e\n\nA controller to override image sources in the event that an image cannot be pulled.\n\n**Want to learn more?** Reach out on [the Slack channel](https://fairwindscommunity.slack.com/messages/saffire) ([request invite](https://join.slack.com/t/fairwindscommunity/shared_invite/zt-e3c6vj4l-3lIH6dvKqzWII5fSSFDi1g)), send an email to `opensource@fairwinds.com`, or join us     for [office hours on Zoom](https://fairwindscommunity.slack.com/messages/office-hours)\n\nBuilt using [kubebuilder](https://github.com/kubernetes-sigs/kubebuilder)\n\n## Alpha Software\n\nAt this time, saffire is currently in _alpha_. This means that we could change literally anything at any time without notice. Keep an eye out for major changes, and hopefully a v1 release at some point.\n\n\n## Why?\n\nThe image repository for docker images is a single point of failure for many clusters. As seen in the past with [rate limiting on Docker Hub]() and several high-profile [Quay.io outages](), these images being unavailable can produce disastrous consequences for Kubernetes cluster operators.\n\nThe intent of saffire is to provide operators with a method of automatically switching image repositories when `imagePullErrors` occur.\n\n## How?\n\nThis works via controller and a CRD called an `AlternateImageSource`. The `AlternateImageSource` specifies a set of `equivalentRepositories`. These repositories *must* have the exact same image tags pushed to them. In order to achieve this, we recommend pushing images to both repositories from your CI pipeline. Here's an example `AlternateImageSource`\n\n```\napiVersion: saffire.fairwinds.com/v1alpha1\nkind: AlternateImageSource\nmetadata:\n  name: alternateimagesource-sample\nspec:\n  imageSourceReplacements:\n    - equivalentRepositories:\n        - quay.io/fairwinds/docker-demo\n        - ehazlett/docker-demo\n```\n\nThis indicates that `quay.io/fairwinds/docker-demo` and `ehazlett/docker-demo` have the exact same image tags in both.\n\nOnce the controller and this `AlternateImageSource` are installed in your cluster, if any pod experiences an `ImgagePullError` in that namespace and the image matches one of these repositories, saffire will find the top level controller of that pod and patch it to set the image as one of the other repositories in the `equivalentRepositories` field (currently this only applies to deployments).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffairwindsops%2Fsaffire","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffairwindsops%2Fsaffire","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffairwindsops%2Fsaffire/lists"}