flowchart LR
W["bioc-web docroot<br/>rrsync over SSH<br/>1.35M files, 447 GB"] -->|"rsync delta, hourly"| Mi["local mirror"]
O["OSN archive<br/>anonymous S3, 4.66 TB"] -->|"one-time copy, verified"| R
Mi -->|"rclone --files-from<br/>+ purge changed URLs"| R[("R2 · bioc-site<br/>1.65M objects, 5.1 TB<br/>private")]
R --> K["Worker<br/>symlink resolution, redirects<br/>ranges, conditional GETs, cache"]
K --> B["browsers"]
K --> RR["R: BiocManager::install()"]
K --> MI["mirror operators<br/>/api/v1/manifest"]
R2 Migration
Full detail lives in the repo’s own README.md and MIGRATION.md — this page is the condensed version for orientation. Don’t duplicate updates here; treat those files as the source of truth and this as a pointer.
Why
Bioconductor’s origin runs off an EBS volume, and crawlers exhaust its IOPS. The traffic that hurts is not page views — it’s bots pulling hundred-megabyte lecture videos out of /help/course-materials/, where a single directory holds 711 MB of .mp4 against 2 MB of HTML, roughly 400:1 from two years of materials alone.
Large immutable files behind a CDN with free egress is what object storage is for. Raising EBS IOPS is the stopgap; this is the replacement.
Architecture
Two filters make the sync affordable: rsync (size+mtime, cheap but coarse) narrows 1.35M files to thousands of candidates, then rclone --checksum (MD5 vs R2 ETag, precise but only affordable over a short list) finds what actually changed. Purging off the second stage — not the first — is what stops a nightly rebuild from purging the whole zone.
Object storage has no symlinks. The docroot has 145, and the Worker resolves them per request against a published _symlinks.json rather than uploading them — so a release roll is data, not a deploy.
Status (measured, not projected)
| R2 | 1,652,759 objects · 5.1 TB · 0 upload errors |
| Docroot | 1,351,530 files · 447 GB |
| OSN archive | 301,217 objects · 4.66 TB · verified, 0 differences |
BiocManager::install() |
8/8 against the mirror |
| Cost | ~$80/month, of which the OSN archive is ~$70 |
Progress since 2026-08-03 (updated 2026-08-13)
The table above is the initial load; the operational picture has moved past it:
- The sync is scheduled and self-checking. An hourly delta sync and a weekly full checksum reconcile run as systemd timers with failure alerting. The most recent weekly pass verified zero drift across 3.7M checked files.
- Request logging is live and monitored — one structured record per request, all fields, no sampling, delivered continuously to durable object storage, queryable side by side with the historical CloudFront logs through a normalizing view, and gap-alerted daily (the delivery pipeline cannot backfill, so gaps are the failure mode that matters). Details in Download Stats.
- The release-roll guard is deployed: scheduled detection of an upstream version roll, with destructive sync actions held for explicit human approval — the class of unattended mass-deletion accident a roll could otherwise cause is fenced off.
- checkResults is mirrored in full for every release; archival releases (≤ 3.22) carry no ongoing sync cost by design.
- The historical
.htaccessbehavior is ported, including the package short-URL rewrites and the container-binaries redirect whose failure mode is silent. - Public installs are invited: Installing packages from the new site is the two-line
BiocManagersetup, verified end-to-end against every repository type.
Verdict: the serving replacement is functionally complete; what remains is the cutover decision. The longer-term layout — which repositories hold what, and the route-by-route redesign behind the Worker — is decided and tracked as a phased migration plan (ADR 0008 in the repo, with per-phase issues).
Not in scope
- AnnotationHub (10.1 TiB) and ExperimentHub (593 GiB) — different service, fetched by their own R packages through a metadata database.
- Anything dynamic — search, BiocViews queries, live build reports. (Note: package landing pages turned out not to be dynamic — see Current State — which simplifies this boundary somewhat.)
Relationship to the decommission plan
This track solves the served docroot’s IOPS/cost problem. It does not by itself remove staging or master — those boxes still build the docroot and (today) serve it. Retiring them is the separate, harder problem covered in the decommission plan.