{"id":28566986,"url":"https://github.com/altinity/docker-sync","last_synced_at":"2025-06-10T15:40:48.900Z","repository":{"id":280121429,"uuid":"792415377","full_name":"Altinity/docker-sync","owner":"Altinity","description":"Keep registries in sync","archived":false,"fork":false,"pushed_at":"2025-06-06T16:33:42.000Z","size":22945,"stargazers_count":3,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-09T05:49:07.220Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","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/Altinity.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-26T16:11:39.000Z","updated_at":"2025-06-06T16:33:44.000Z","dependencies_parsed_at":"2025-03-01T11:45:23.437Z","dependency_job_id":"f03e5858-6039-43f8-ac46-e41d5a6ccbdd","html_url":"https://github.com/Altinity/docker-sync","commit_stats":null,"previous_names":["altinity/docker-sync"],"tags_count":57,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Altinity%2Fdocker-sync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Altinity%2Fdocker-sync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Altinity%2Fdocker-sync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Altinity%2Fdocker-sync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Altinity","download_url":"https://codeload.github.com/Altinity/docker-sync/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Altinity%2Fdocker-sync/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259104328,"owners_count":22805857,"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":[],"created_at":"2025-06-10T15:40:19.132Z","updated_at":"2025-06-10T15:40:48.874Z","avatar_url":"https://github.com/Altinity.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# docker-sync\n\nSimple tool to keep images in sync between different registries.\n\nIt uses the OCI distribution spec to pull and push images, so it should work with any compliant registry.\n\nCurrently, it doesn't support the deprecated v1 manifests, and there are no plans to support them.\n\n## Usage\n\nClone the repository:\n\n```console\ngit clone https://github.com/Altinity/docker-sync.git\n```\n\nChange to the project directory:\n\n```console\ncd docker-sync\n```\n\nBuild the project:\n\n```console\nmake\n```\n\nTo run a one-off synchronization, you can run `dist/docker-sync sync`.\n\n```sh\ndist/docker-sync --source docker.io/library/ubuntu --target r2:blablabla:docker-sync-test:ubuntu --source-username foo --source-password bar --target-username foo --target-password bar\n```\n\nRun `dist/docker-sync sync --help` for more configuration options.\n\n## Configuration\n\nWrite the default config file:\n\n```console\ndist/docker-sync writeConfig -o config.yaml`\n```\n\nEdit the config file accordingly, then run:\n\n```console\ndist/docker-sync\n```\n\nThe default configuration looks like this:\n\n```yaml\necr:\n  region: us-east-1\nlogging:\n  colors: true\n  format: text\n  level: INFO\n  output: stdout\n  timeformat: \"15:04:05\"\nsync:\n  images:\n    - source: docker.io/library/ubuntu\n      targets:\n        - docker.io/kamushadenes/ubuntu\n  interval: 5m\n  maxerrors: 5\n  registries:\n    - auth:\n        helper: \"\"\n        password: \"\"\n        token: \"\"\n        username: \"\"\n      name: Docker Hub\n      url: docker.io\ntelemetry:\n  enabled: false\n  metrics:\n    exporter: prometheus\n    prometheus:\n      address: 127.0.0.1:9090\n      path: /metrics\n    stdout:\n      interval: 5s\n```\n\nThe `sync` section is where you define the images you want to keep in sync. The `interval` is the time between syncs, and `maxerrors` is the maximum number of errors before the sync is stopped and the program exits.\n\n### Authentication\n\nTo provide authentication for registries, put them under `sync.registries` in the following format:\n\n```yaml\nsync:\n  registries:\n    - auth:\n        helper: \"\"\n        password: \"\"\n        token: \"\"\n        username: \"\"\n      name: Docker Hub\n      url: docker.io\n```\n\n#### ECR\n\nTo authenticate against ECR, you can leave `password`, `token` and `username` empty, and set `helper` to `ecr`:\n\n```yaml\nsync:\n  registries:\n    - auth:\n        helper: ecr\n        password: \"\"\n        token: \"\"\n        username: \"\"\n      name: ECR\n      url: 123456789012.dkr.ecr.us-east-1.amazonaws.com\n```\n\nThe same applies to ecr-public, since it uses the url prefix to differentiate between the two.\n\nNow, any image under `123456789012.dkr.ecr.us-east-1.amazonaws.com` will be authenticated using the default AWS credentials.\n\n#### GCR\n\nTo authenticate against ECR, get either a access token or service account key.\n\n##### Access Token\n\nCheck the [GCR documentation](https://cloud.google.com/artifact-registry/docs/docker/authentication#token) for more information.\n\nNote that access tokens are short lived.\n\n```yaml\nsync:\n  registries:\n    - auth:\n        helper: \"\"\n        password: \"PASSWORD\"\n        token: \"\"\n        username: \"oauth2accesstoken\"\n      name: GCR / GAR\n      url: gcr.io\n```\n\n##### Service Account Key\n\nCheck the [GCR documentation](https://cloud.google.com/artifact-registry/docs/docker/authentication#json-key) for more information.\n\n```yaml\nsync:\n  registries:\n    - auth:\n        helper: \"\"\n        password: \"BASE64_ENCODED_JSON_KEY\"\n        token: \"\"\n        username: \"_json_key_base64\"\n      name: GCR / GAR\n      url: gcr.io\n```\n\n#### R2 (target only)\n\n```yaml\nsync:\n    images:\n        - source: docker.io/library/ubuntu\n          targets:\n            - r2:f6934f56ce237241104dbe9302cee786:docker-sync-test:ubuntu # r2:\u003cendpoint\u003e:\u003cbucket\u003e:\u003cimage\u003e\n      registries:\n            - auth:\n                helper: \"\"\n                password: \"SECRET_ACCES_KEY\"\n                token: \"\"\n                username: \"ACCESS_KEY_ID\"\n              name: R2\n              url: r2:f6934f56ce237241104dbe9302cee786:docker-sync-test # r2:\u003cendpoint\u003e:\u003cbucket\u003e\n```\n\nNote that pulls should be performed against the bucket's public url. Check [the docs](https://developers.cloudflare.com/r2/buckets/public-buckets/#enable-managed-public-access) for more information.\n\nDon't use the standard `r2.dev` domain, as some rules need to be created and they won't work without a custom domain.\n\nTo match the [official spec](https://github.com/openshift/docker-distribution/blob/master/docs/spec/api.md#api-version-check), some rules need to be created.\n\nUse the Cloudflare UI to create the rules by going to Rules \u003e Transform Rules.\n\n##### V2 Ping Fix - /v2/ requires `200 OK`\n\nCreate a **Rewrite URL** rule.\n\n```\n**If incoming requests match...**: Custom filter expression\n\n**URI Path**: `equals` `/v2/`\n\n**Expression Preview**: `(http.request.uri.path eq \"/v2/\")` (optionally also add your hostname for a better match)\n\n**Then...**: Path \u003e Rewrite to... \u003e Static \u003e `/v2` (without the trailing slash)\n```\n\n##### V2 Ping Fix - /v2/ requires `Docker-Distribution-API-Version` header\n\nCreate a **Modify Response Header** rule.\n\n```\n**If incoming requests match...**: Custom filter expression\n\n**URI Path**: `starts with` `/v2/`\n\n**Expression Preview**: `(starts_with(http.request.uri.path, \"/v2\"))` (optionally also add your hostname for a better match)\n\n**Then...**: Set static \u003e `Docker-Distribution-API-Version` \u003e `registry/2.0`\n```\n\n##### Content-Type Fix\n\nCreate a **Modify Response Header** rule.\n\n```\n**If incoming requests match...**: Custom filter expression\n\n**Expression**: `(starts_with(http.request.uri.path, \"/v2/\") and (http.request.uri.path contains \"/manifests/\" or http.request.uri.path contains \"/blobs/\"))`\n\n**Then...**: Remove\u003e `Content-Type`\n```\n\n#### S3 (target only)\n\n```yaml\nsync:\n    images:\n        - source: docker.io/library/ubuntu\n          targets:\n            - r3:us-east-1:docker-sync-test:ubuntu # s3:\u003cregion\u003e:\u003cbucket\u003e:\u003cimage\u003e\n      registries:\n            - auth:\n                helper: \"\"\n                password: \"SECRET_ACCES_KEY\"\n                token: \"\"\n                username: \"ACCESS_KEY_ID\"\n              name: S3\n              url: s3:us-east-1:docker-sync-test # s3:\u003cregion\u003e:\u003cbucket\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faltinity%2Fdocker-sync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faltinity%2Fdocker-sync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faltinity%2Fdocker-sync/lists"}