Skip to content
~/tosaki
Go back

OSS Migration Log: Moving Image Hosting to Cloudflare R2

Edit page

Setting up a blog naturally necessitates an image hosting solution (“image bed”). My chosen solution is Cloudflare-ImgBed. As for the storage backend, I initially used a Telegram Channel Bot to exploit their free storage, but after learning that this violates the TOS and carries a risk of account suspension, I switched to standard Object Storage (OSS).

Instead of jumping straight to Cloudflare R2, I initially chose a third-party provider, Tebi.io, because it offered a generous free tier, required no credit card binding, and posed no risk of unexpected billing traps. I thought I could sit back and relax, but recently I received a notification: Tebi.io’s OSS service will be shutting down at the end of March 2026. My image host serves not only my personal blog but also my friend’s ylqian.com and various forum discussions. Therefore, I had to execute a “seamless migration,” targeting the stable and reliable Cloudflare R2 as the new destination.

File Migration

Migrating the file content was straightforward; a simple rclone sync command did the job. I believe most LLMs can provide a standard operating procedure for this step.

Backend Migration & Metadata Reconstruction

The real challenge lay in migrating the image host’s backend. After synchronizing the OSS data, the image host’s built-in “index reconstruction” function failed to index all the images (likely due to the Cloudflare KV write limits mentioned below). Furthermore, a simple reconstruction could not recover the index for images originally sourced from Telegram.

Fortunately, this image hosting service supports configuration backup and restoration, and the backup files contain the metadata (including the source storage address) for every image. I wrote a simple script, migrate_tebi_2_r2.py, to batch-replace the storage addresses in the backup file.

I assumed that simply importing the modified backup would suffice, but the restoration process failed with a timeout error after 30 seconds. The root cause was that I had over 700 images, and Cloudflare KV (used as the metadata storage backend) is not designed for high-frequency writes (it is optimized for ultra-high-frequency reads and low-frequency writes). Even after I asked an LLM to optimize the script to support concurrent writes, it still threw an error: KV put() limit exceeded for the day. The free daily write limit for KV is only 1,000 requests, which I had exhausted during my repeated debugging and restoration attempts.

Switching to Cloudflare D1

Luckily, the image host backend also supports Cloudflare D1 storage. Compared to KV, D1 is closer to a traditional relational database and has much looser write restrictions. Although its read performance is theoretically slightly lower than KV’s, it is completely sufficient for a personal image hosting scenario. After switching to D1, the restoration work was resolved effortlessly.

Summary

The image host has now been successfully migrated to R2. Unless there are major changes to Cloudflare Workers or R2 in the future, no further tinkering should be needed in the short term. The biggest lesson learned from this migration is: Always choose a stable and reliable service provider for your infrastructure from the start. Once a provider runs away or shuts down, the time cost of migration and the potential risk of data loss far outweigh the small amount of money saved initially.


Edit page
Share this post on:

Previous Post
AI Secondary Display (Part 1): Reviving a Forgotten Kindle, Crafting an Elegant E-Ink Desktop Dashboard
Next Post
No Longer Writing for Utility: Upgrading Both Content and Framework