Un bucket de R2 por tenant compensa por la residencia y el borrado

El almacenamiento por tenant en Cloudflare R2 es la elección entre dar a cada cliente de un SaaS su propio bucket o darle un prefijo de claves dentro de un bucket compartido. De esa elección dependen el alta, la forma de facturar el espacio, el lugar físico donde están los archivos de un cliente y la prueba de que los has borrado cuando se va.
La pregunta llega el día en que el producto deja subir archivos. El primer impulso suele ser un bucket por cliente, creado por API al abrir la cuenta. Funciona, y hoy R2 lo hace barato hasta cierto punto. Pero mete en el flujo de alta una llamada a la API de Cloudflare, una regla de nombres y un límite de frecuencia. Aquí comparamos los dos modelos con los límites que R2 publica a noviembre de 2026, con los pasos para automatizar cada uno.
¿Con qué modelo conviene empezar?
Con un prefijo por tenant, en un bucket compartido por cada jurisdicción de datos. El bucket propio tiene sentido cuando un contrato pide algo que solo da un bucket: archivos guardados en la Unión Europea, una línea de almacenamiento en la factura sacada de las cifras de Cloudflare, un dominio propio por cliente, o un borrado que puedes demostrar con un bucket que ya no existe. Casi todos los productos usan el primer modelo para todos los clientes y el segundo para unos pocos.
Los dos modelos, uno al lado del otro
- Alta. Prefijo: nada que crear, la primera subida escribe bajo
tenants/<tenant-id>/. Bucket: una llamada CreateBucket y luego la configuración de CORS y lifecycle. - Techo. Prefijo: objetos ilimitados por bucket. Bucket: 100.000 incluidos por cuenta, luego 0,10 $ al mes por cada bucket adicional.
- Aislamiento. Prefijo: lo garantizan tu código y credenciales limitadas al prefijo. Bucket: lo garantiza el propio límite del bucket.
- Consumo por cliente. Prefijo: un registro que llevas tú. Bucket: las métricas de almacenamiento de R2, por nombre de bucket.
- Ubicación de los datos. Prefijo: se fija una vez por bucket compartido. Bucket: se fija por cliente al crearlo y ya no cambia.
- Baja del cliente. Prefijo: borras cada clave bajo el prefijo. Bucket: lo vacías y luego lo eliminas.
¿Qué permite R2 por cuenta en 2026?
Cuatro cifras de la documentación de Cloudflare pesan en la decisión.
- Buckets. No hay un techo fijo: el límite por defecto es de 100.000 buckets por cuenta y cada bucket por encima cuesta 0,10 $ al mes (límites de R2, precios de R2). Con 250.000 tenants y un bucket cada uno, esa línea suma 15.000 $ al mes antes de guardar un solo byte.
- Frecuencia de la API REST. La API REST de Cloudflare acepta 1.200 peticiones cada cinco minutos, sumando todas las operaciones de R2 de la cuenta. Si te pasas, se bloquean todas las llamadas durante los cinco minutos siguientes, panel incluido (límites de la API de Cloudflare). Un pico de altas que crea buckets por REST puede dejarte fuera de tu propio panel.
- Gestión de buckets. Crear, borrar, listar y configurar tienen un techo de 50 operaciones por segundo por bucket. Las lecturas y escrituras de objetos no cuentan.
- Tokens de API. Una cuenta admite como máximo 500 tokens de API de cuenta. Una clave S3 permanente por cliente deja de funcionar en el cliente 501.
Los nombres de bucket añaden una condición: minúsculas, cifras y guiones, de 3 a 63 caracteres, sin guion al principio ni al final (crear buckets). Un id de tenant en formato UUID cabe, con sus 36 caracteres. El nombre de una empresa hay que limpiarlo antes, y dos empresas pueden acabar en la misma cadena.
Dónde gana el prefijo por tenant
El alta se queda en tu base de datos
Al abrir la cuenta no se crea nada en R2. La primera subida escribe en tenants/<tenant-id>/ y desde ese momento el prefijo existe. Ninguna llamada externa puede fallar a mitad del onboarding, así que no queda ningún cliente creado a medias que arreglar.
Sin techo y sin coste por bucket
Un bucket admite objetos ilimitados. Un producto con plan gratuito y muchas cuentas inactivas paga solo los bytes que esas cuentas ocupan, 0,015 $ por GB al mes en almacenamiento Standard, con la salida de datos gratis en los dos modelos.
El acceso limitado sigue funcionando
El aislamiento pasa del límite del bucket a la credencial. Las credenciales temporales de R2 parten de un token padre, valen para un único bucket con un nivel de permisos y, si quieres, una lista de prefijos, y caducan como mucho a los 604.800 segundos, es decir, siete días (credenciales temporales). Una credencial limitada a tenants/acme/ no puede leer tenants/globex/. Para una sola subida o descarga desde el navegador basta una URL prefirmada: una operación sobre un objeto, válida de un segundo a siete días (URLs prefirmadas).
POST /accounts/{account_id}/r2/temp-access-credentials
{
"bucket": "tenants-default",
"parentAccessKeyId": "<parent key id>",
"permission": "object-read-write",
"prefixes": ["tenants/acme/"],
"ttlSeconds": 900
}Cada llamada a ese endpoint gasta una de las 1.200 peticiones de los cinco minutos. Para emitir muchas credenciales cortas, Cloudflare documenta una vía local: tu backend firma un JWT con el secreto del token padre y deriva la credencial por su cuenta, sin llamar a Cloudflare. Si revocas el token padre, todas las credenciales derivadas dejan de funcionar al instante.
Dónde gana el bucket por tenant
Residencia de datos por cliente
La jurisdicción es una propiedad del bucket. R2 ofrece eu, us y fedramp, garantiza que los objetos se quedan dentro de la elegida y no deja cambiarla después de crear el bucket (ubicación de los datos). Con prefijos, los clientes europeos comparten un bucket de la UE. Con buckets, cada cliente tiene la jurisdicción que pone su contrato. En los dos casos hace falta un cliente S3 por jurisdicción: un bucket con jurisdicción solo responde en su propio endpoint, https://<ACCOUNT_ID>.eu.r2.cloudflarestorage.com para la UE (autenticación de R2).
Consumo por cliente con las cifras de Cloudflare
Las métricas de almacenamiento de R2 dan el tamaño y el número de objetos por nombre de bucket, a través de la GraphQL Analytics API, en ventanas de hasta 31 días (métricas de R2). Con un bucket por cliente, la línea de almacenamiento de la factura sale de las mismas cifras con las que Cloudflare te factura a ti. Con prefijos, el registro lo llevas tú: guardas el tamaño de cada objeto al escribirlo y lo restas al borrarlo.
Configuración por cliente
Las reglas de CORS, las de lifecycle, las notificaciones de eventos y hasta 100 dominios propios se configuran en el bucket. Un cliente que sirve los archivos desde su propio dominio, o que necesita un plazo de conservación distinto al del resto, se gestiona mejor con un bucket suyo.
Un borrado que se puede demostrar
La baja de un cliente cuesta más o menos lo mismo en los dos modelos, porque R2 solo elimina buckets vacíos. Primero se van todos los objetos, con la acción Empty del panel, un bucle desde un cliente o una regla de lifecycle que hace caducar todo a un día (eliminar buckets). DeleteObject y DeleteBucket son gratuitas, y listar las claves es una operación de Clase A a 4,50 $ por millón. La diferencia está en el rastro que queda. «Bucket eliminado en esta fecha» responde en una línea a una petición de borrado. Un prefijo te deja un registro de claves borradas.
¿Cómo se automatiza la creación de un bucket de R2 en el alta?
Cuando un cliente necesita de verdad un bucket, su creación va en un trabajo en segundo plano, nunca dentro de la petición de alta.
- Saca el nombre del id del tenant. Por ejemplo
t-seguido del UUID del tenant, en minúsculas. Guárdalo en la fila del tenant antes de llamar a Cloudflare, para que un reintento apunte al mismo nombre. - Créalo con la API compatible con S3. CreateBucket, con un token Admin Read & Write, en el endpoint de la jurisdicción que necesita el cliente. Cloudflare aplica el techo de 1.200 peticiones a su API REST y remite el trabajo de mucho volumen a la API S3. Cada PutBucket se cobra como operación de Clase A.
- Aplica la configuración en el mismo trabajo. CORS para los orígenes de la app y las reglas de lifecycle que incluya el plan. También son operaciones de Clase A.
- Encola y limita el ritmo. Una cola a velocidad fija, y ante un 429 esperas los segundos que indica la cabecera
retry-afterantes de reintentar. - Da el acceso por sesión. Un solo token padre, del que emites credenciales temporales ligadas al bucket del cliente. Un token por cliente choca con el límite de 500.
- Marca el tenant como listo. Las subidas siguen desactivadas hasta que el trabajo guarda en la fila del tenant el nombre del bucket y un indicador de listo.
Los nombres sacados del id también evitan una trampa discreta. R2 respeta un Location Hint solo la primera vez que se crea un nombre de bucket: si borras un bucket y lo vuelves a crear con el mismo nombre, vuelve a su ubicación original.
Qué usamos y por qué
En los productos multi-tenant que desarrollamos, el punto de partida es un bucket compartido por jurisdicción, por ejemplo tenants-default y tenants-eu, con un prefijo por cliente y la jurisdicción guardada en la fila del tenant. El backend firma las URLs prefirmadas de subidas y descargas, y en cada subida escribe el tamaño del objeto en una tabla de consumo. El bucket propio lo reservamos para los clientes que quieren su propio dominio, su propia regla de conservación o un borrado auditable, y lo crea el trabajo descrito arriba.
El precio que aceptamos es que el aislamiento vive en el código. Basta con que falte una comprobación de tenant en una ruta que firma URLs para exponer los archivos de otro cliente. Por eso cada firma lee el tenant de la sesión, nunca del cuerpo de la petición, y un test intenta firmar una clave bajo el prefijo de otro tenant y espera un rechazo. La misma disciplina vale para las filas de la base de datos, que tratamos en Supabase RLS a escala; los motivos para construir multi-tenant desde el principio están en Multi-tenant desde el primer día. Si la elección de R2 frente a S3 o Supabase Storage sigue abierta, empieza por nuestra comparativa de los tres.
Preguntas frecuentes
Los primeros 100.000 buckets de una cuenta están incluidos, y cada uno por encima cuesta 0,10 $ al mes. Crear un bucket es una llamada PutBucket, que se cobra como operación de Clase A a 4,50 $ por millón, con un millón de operaciones de Clase A gratis cada mes. El almacenamiento cuesta lo mismo en los dos modelos: 0,015 $ por GB al mes en Standard a partir de los primeros 10 GB, sin coste de salida. Un producto con 150.000 tenants en buckets propios paga 50.000 veces 0,10 $, es decir, 5.000 $ al mes, solo por los buckets.
Es tan seguro como el código que concede el acceso. Una credencial temporal limitada a un prefijo no puede leer claves fuera de él, y una URL prefirmada cubre un solo objeto durante un tiempo fijo, así que el riesgo está en la ruta que las emite. El tenant se lee de la sesión autenticada, nunca de la petición, y un test comprueba que se rechaza la petición de una clave de otro tenant. Si un contrato exige separación física o una jurisdicción concreta, un bucket propio lo resuelve de forma más directa. Las dudas sobre obligaciones del RGPD corresponden a tu delegado de protección de datos o a un asesor legal.
Sí, y el cambio sale barato si las claves de los objetos se guardan relativas a la raíz del tenant. Creas el bucket con el trabajo descrito arriba, copias los objetos con una herramienta S3 como rclone o con un Worker vinculado a los dos buckets, cambias el nombre del bucket en la fila del tenant y borras el prefijo antiguo. Las subidas durante la copia necesitan una pausa breve o una segunda pasada. La jurisdicción no cambia después de crear el bucket, así que un tenant europeo pasa del bucket compartido de la UE a un bucket nuevo creado en la jurisdicción de la UE.
Artículos relacionados
2 de mayo de 2026 · 8 min de lectura
28 de mayo de 2026 · 10 min de lectura
14 de septiembre de 2026 · 8 min de lectura