{"id":"IL-0378","org":"Cloudflare","date":"2025-03-21","docs":[],"title":{"en":"All Cloudflare R2 object writes and about 35% of reads failed when the R2 Gateway could not authenticate to storage","ko":"Cloudflare R2 객체 스토리지에서 1시간 7분 동안 쓰기가 모두 실패하고 읽기가 전 세계에서 약 35% 실패했다"},"domain":["database"],"impact":{"scope":"R2 object writes (100% failed) and object reads (about 35% failed globally), and services that use R2: Billing invoices, Cache Reserve, Email Security metrics, Images, Key Transparency Auditor, Log Delivery, Stream and Vectorize","users":"All customers using Cloudflare R2","region":"Global"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Because the deletion of the old credentials took effect gradually, availability metrics were a poor way to confirm the change. Cloudflare said the team should have checked directly which token the R2 Gateway service was using to authenticate with the storage infrastructure instead of relying on those metrics.","ko":"이번 장애는 사람의 실수로 일어났고, R2 Gateway Worker 가 어떤 자격 증명으로 스토리지 인프라에 인증하는지 볼 수 없어서 더 길어졌다. Cloudflare 는 옛 자격 증명을 지운 뒤 가용성 지표에 기대지 말고, R2 Gateway 서비스가 실제로 어떤 토큰을 쓰는지 직접 확인했어야 했다고 적었다."},"related":[],"sources":[{"url":"https://blog.cloudflare.com/cloudflare-incident-march-21-2025/","kind":"blog","archived":"https://web.archive.org/web/20260908135604/https://blog.cloudflare.com/cloudflare-incident-march-21-2025/"}],"summary":{"en":"R2 is made of three parts: the production gateway Worker that serves the S3 API, REST API and Workers API, a metadata service, and storage infrastructure that holds the encrypted object data. The R2 Gateway Worker authenticates to the storage infrastructure with a credential ID and key pair that Cloudflare rotates regularly. Production and development are separate Workers environments, each an isolated Worker with its own environment variables and secrets, and both the wrangler secret put and wrangler deploy commands use the default environment when the --env parameter is left out.\n\nOn 21 March 2025 the R2 engineering team began a rotation at 19:49 UTC by creating new credentials while keeping the old ones. At 20:19 UTC the team set the new secret and deployed the gateway, but both commands were run without --env production, so the new credentials reached the Worker of the default environment while the production Worker kept the old ones. Believing production had been updated, the team removed the old credentials from the storage infrastructure at 20:37 UTC. As that deletion propagated, the production R2 Gateway Worker could no longer authenticate with the storage backend, and from 21:38 UTC writes failed and many reads failed; an intermediate cache in front of storage kept serving a large share of reads.\n\nAfter alerts at 21:45 UTC and an internal incident declared at 21:50 UTC, the team suspected a credential propagation regression and twice made new credentials, the second set deployed again to the wrong Worker. At 22:36 UTC a review of the production Worker's release history showed that the credentials had gone to a non-production Worker. The team deployed them to the production Worker and R2 availability recovered at 22:45 UTC. No data was lost or corrupted: uploads and changes that had returned a successful HTTP status were stored. Cloudflare said the incident came from human error and lasted longer because it could not see which credentials the gateway Worker was using.","ko":"R2 Gateway 서비스는 R2 의 API 프런트엔드이고, ID 와 키 쌍으로 된 자격 증명으로 스토리지 인프라에 인증한다. R2 엔지니어링 팀은 이 자격 증명을 정기적으로 교체한다. 2025년 3월 21일 19:49 UTC 에 팀은 교체를 시작해 새 자격 증명을 만들었다. 20:19 UTC 에 wrangler secret put 으로 새 비밀 값을 넣고 wrangler deploy 로 R2 Gateway 서비스를 배포했다. 두 명령 모두 --env 인자를 빠뜨렸고, 이 명령들은 --env 가 없으면 기본 환경을 쓴다. 그래서 새 자격 증명은 운영 환경 Worker 가 아니라 기본 환경 Worker 에 들어갔지만, 팀은 운영 Worker 가 갱신됐다고 믿었다. 20:37 UTC 에 교체를 마무리하려고 스토리지 인프라에서 옛 자격 증명을 지웠다. 운영 R2 Gateway Worker 는 여전히 옛 자격 증명을 쓰고 있었으므로 스토리지 백엔드에 인증하지 못하게 됐다.\n\n삭제가 스토리지 인프라에 퍼지는 데 시간이 걸려서 영향은 21:38 UTC 에 천천히 시작됐다. 21:45 UTC 에 가용성 알림이 울렸고, 21:50 UTC 에 내부 장애를 선언했고, 22:05 UTC 에 공개 상태 페이지를 올렸다. 팀은 처음에 새 자격 증명이 퍼지는 과정에 회귀가 생겼다고 의심해 자격 증명을 다시 만들었다. 그다음 새 자격 증명을 게이트웨이에 다시 배포했지만, 이때도 환경 인자를 빠뜨려 운영이 아닌 Worker 에 배포했다. 22:36 UTC 에 운영 Worker 의 배포 이력을 보다가 자격 증명이 운영이 아닌 Worker 에 들어갔다는 것을 찾았다. 22:45 UTC 에 올바른 운영 Worker 에 자격 증명을 배포하자 가용성이 바로 돌아왔고, 22:54 UTC 에 해결로 닫았다.\n\n장애 동안 R2 의 쓰기는 100%, 읽기는 전 세계에서 약 35% 실패했다. 스토리지 앞에 있는 중간 캐시가 계속 요청을 처리해서 읽기 피해가 크게 줄었다. Billing, Cache Reserve, Email Security, Images, Key Transparency Auditor, Log Delivery, Stream, Vectorize 도 영향을 받았다. 데이터 손실이나 손상은 없었고, 성공 HTTP 상태 코드를 받은 업로드와 변경은 모두 저장됐다."},"trigger":{"en":"During a routine rotation of the credentials the R2 Gateway Worker uses to authenticate with the storage infrastructure, the new credentials were deployed to the Worker of the default environment instead of the production Worker, and the old credentials were then removed from the storage infrastructure at 20:37 UTC.","ko":"R2 엔지니어링 팀이 R2 Gateway 서비스의 스토리지 인프라 자격 증명을 교체하면서 wrangler secret put 과 wrangler deploy 에 --env 인자를 빠뜨렸다. 새 자격 증명은 운영 Worker 가 아니라 기본 환경 Worker 에 들어갔고, 그 뒤 스토리지 인프라에서 옛 자격 증명을 지웠다."},"severity":"unknown","symptoms":["credentials-unavailable","high-error-rate"],"timeline":{"detected_after":"7m","resolved_after":"1h 7m"},"date_kind":"incident","detection":{"en":"R2 global availability alerts fired at 21:45 UTC, seven minutes after impact began, at a 2% error budget burn rate, and the R2 engineering team turned to operational dashboards and logs.","ko":"21:45 UTC 에 R2 전역 가용성 알림이 울렸다. 알림은 오류 예산 소진율 2% 를 가리켰고, R2 엔지니어링 팀은 운영 대시보드와 로그를 보기 시작했다."},"components":["object-storage"],"root_cause":"human-operation","remediation":[{"text":{"en":"Deployed the new credentials to the production R2 Gateway Worker, which restored R2 availability","ko":"22:45 UTC 에 올바른 운영 R2 Gateway Worker 에 자격 증명을 배포했고, R2 가용성이 돌아왔다."}}],"action_items":[{"text":{"en":"Add logging tags with the suffix of the credential ID the R2 Gateway Worker uses","ko":"R2 Gateway Worker 가 스토리지 인프라 인증에 쓰는 자격 증명 ID 의 끝자리를 로그 태그에 넣어, 어떤 자격 증명을 쓰는지 바로 확인할 수 있게 했다."}},{"text":{"en":"Require confirmation that the new token ID suffix matches storage infrastructure logs before deleting the old token","ko":"옛 토큰을 지우기 전에 새 토큰 ID 의 끝자리가 스토리지 인프라 로그와 맞는지 반드시 확인하도록 내부 절차를 바꿨다."}},{"text":{"en":"Run key rotation through the hotfix release tooling, which enforces the environment, instead of manual wrangler commands","ko":"키 교체를 wrangler 명령을 손으로 치는 대신 핫픽스 배포 도구로만 하도록 했다. 이 도구는 환경 설정을 강제하고 다른 안전 검사도 한다."}},{"text":{"en":"Write the two-person validation of these changes into the standard operating procedures","ko":"변경을 적어도 두 사람이 확인한다는 관행을 관련 표준 운영 절차에 명시했다."}},{"text":{"en":"Extend the closed loop health check system to test new keys, report their status through alerting and confirm global propagation before releasing the gateway Worker","ko":"엔드포인트를 감시하는 기존 폐루프 상태 검사 시스템을 넓힌다. 이 시스템이 새 키를 시험하고, 그 상태를 알림 플랫폼으로 자동 보고하고, 게이트웨이 Worker 를 배포하기 전에 새 키가 전역으로 퍼졌는지 확인하게 한다."}},{"text":{"en":"Add observability views of upstream success rates that bypass caching","ko":"분산 스토리지 엔드포인트의 문제를 빨리 가려내도록, 관측 플랫폼에 캐시를 거치지 않은 업스트림 성공률 화면을 더한다."}}],"contributing":["assumption-unverified","monitoring-blind-spot","delayed-effect-change"],"recurrence_of":null,"symptoms_text":{"en":"From 21:38 UTC on 21 March 2025, Cloudflare R2 object storage returned errors for 1 hour and 7 minutes: all object writes failed and about 35% of object reads failed globally. R2 global availability alerts fired at 21:45 UTC, at a 2% error budget burn rate.\n\nOperations that touch only metadata, such as head and list, kept working, and cached object reads were not affected, so customers reading public assets through custom domains saw fewer errors. Services built on R2 failed with it: all Images and Stream uploads failed, successful delivery of stored images fell to about 25%, Stream video segment delivery fell to 94% and Stream Live was down, all Key Transparency Auditor operations failed, all Vectorize insert and upsert operations failed, Logpush and Logpull log delivery was delayed by up to 70 minutes, Cache Reserve sent more requests to customer origins, customers could get errors when downloading past invoices, and Email Security metrics stopped updating.\n\nThe drop in availability was gradual and not obvious at first, because the deletion of the old credentials reached the storage infrastructure with a delay. Seeing a slow but steady decline in both read and write availability, the R2 engineering team suspected a regression in how new credentials propagate through the storage infrastructure. At 22:15 UTC the team created another set of credentials to force propagation again, with no improvement, and at 22:30 UTC it deployed a fresh set to the R2 Gateway Worker to check whether the credentials themselves were at fault; that deploy also lacked the environment parameter and went to the same non-production Worker.","ko":"21:45 UTC 에 R2 전역 가용성 알림이 울렸다. 알림은 오류 예산 소진율 2% 를 가리켰고, R2 의 읽기와 쓰기 가용성 지표가 조금씩 계속 내려가고 있었다.\n\n가용성 지표는 21:38 UTC 부터 나빠지기 시작했다. 스토리지 인프라에서 옛 자격 증명을 지운 것이 퍼지는 데 시간이 걸려서 지표는 한꺼번에 떨어지지 않고 서서히 내려갔다. 그래서 처음에는 문제가 바로 눈에 띄지 않았다.\n\n객체 쓰기는 100% 실패했고, 객체 읽기는 전 세계에서 약 35% 실패했다. head 와 list 같은 메타데이터만 쓰는 작업은 영향을 받지 않았다. 사용자 지정 도메인으로 공개 객체를 읽는 고객은 캐시된 객체 읽기가 성공해서 오류율이 더 낮았다.\n\nR2 에 기대는 다른 서비스도 실패했다. Images 와 Stream 은 업로드가 모두 실패했고, Stream Live 는 내려갔다. Vectorize 는 insert 와 upsert 가 모두 실패했다. Log Delivery 는 로그 처리가 최대 70분 늦어졌고, Cache Reserve 고객의 오리진으로 가는 요청이 늘었다.\n\nR2 엔지니어링 팀은 가용성이 서서히 떨어지는 것을 보고 스토리지 인프라에서 새 자격 증명이 퍼지는 과정에 회귀가 생겼다고 의심했다. 22:15 UTC 에 다시 퍼지게 하려고 스토리지 인프라용 자격 증명을 새로 만들었지만 22:20 UTC 까지 지표는 나아지지 않았다.\n\n22:30 UTC 에 팀은 게이트웨이에 넣은 자격 증명이 문제인지 확인하려고 새 자격 증명을 R2 Gateway Worker 에 배포했다. 처음 교체 때처럼 이번에도 wrangler secret put 과 wrangler deploy 에 환경 인자를 빠뜨려서, 새 자격 증명은 운영이 아닌 Worker 에 배포됐다."},"updated_at":"2026-11-14T12:34:02.968470Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0378","org_slug":"cloudflare"}