wandres.dev
REMOTE CACHING · compartir la caché

Turborepo con Vercel Remote Cache: login, link y tokens en CI

El camino de menor fricción para activar remote caching: en tu máquina, turbo login y turbo link contra la caché alojada de Vercel; en CI, donde no hay login interactivo, las variables TURBO_TOKEN y TURBO_TEAM. Gratis para uso personal, de pago para equipos, y cero infraestructura que operar. Con un apunte de seguridad para los PRs no confiables.

⏱ 16 min

La forma más rápida de pasar de la teoría a una caché compartida real es la caché alojada de Vercel, el destino por defecto de Turborepo y de cero infraestructura para ti. En tu máquina son dos comandos: turbo login autentica contra Vercel, y turbo link asocia el repositorio con la caché remota de un equipo. A partir de ahí, cada turbo run sube y baja artefactos solo. En CI, donde no hay navegador ni login interactivo, el mismo enlace se establece con dos variables de entorno, TURBO_TOKEN y TURBO_TEAM. Gratis para uso personal, de pago para equipos.

🎯 Al terminar esta lección sabrás
  • Activar la caché remota en tu máquina con turbo login y turbo link.
  • Conectar CI de forma no interactiva con TURBO_TOKEN y TURBO_TEAM.
  • Verificar que los aciertos remotos ocurren de verdad y saber leerlos.
  • Blindar la caché compartida frente a PRs no confiables con modo de solo lectura.

La caché remota de Vercel es la opción de configuración cero: no hay servidor que levantar ni almacenamiento que aprovisionar. Se activa con dos comandos. El primero autentica tu CLI contra tu cuenta de Vercel; abre el navegador, completas el flujo, y queda un token guardado localmente.

turbo login
turbo link

El segundo, turbo link, asocia este repositorio con la caché remota de un ámbito concreto —tu cuenta personal o un equipo—. Escribe un .turbo/config.json con el identificador del equipo y el endpoint de la API; ese archivo no contiene secretos y puede comitearse sin problema. Desde ese momento, cualquier turbo run build consulta la caché remota antes de compilar: si otra máquina ya subió el artefacto para ese hash, lo descarga y reproduce; si no, compila y lo sube para quien venga después. La primera corrida siembra la caché; todas las siguientes, tuyas o de tus compañeros, la cosechan.

Merece la pena entender qué guarda turbo link y qué no. Escribe el identificador del equipo y el endpoint en .turbo/config.json —datos no sensibles, comiteables—, pero no guarda ahí el token: ese vive aparte, en la configuración global de tu usuario tras el turbo login. La separación es deliberada. La asociación entre el repositorio y el equipo es pública dentro de tu organización; la credencial que autoriza a leer y escribir en la caché es personal y no debe viajar jamás en el repositorio. Esa misma distinción entre “a qué equipo pertenezco” y “con qué permiso entro” reaparece, amplificada, en el momento de conectar CI.

ℹ️
Gratis personal, de pago en equipo, cero operaciones

La caché alojada de Vercel es gratuita para uso personal y de pago cuando la comparte un equipo, pero en ambos casos el trato es el mismo: tú no operas nada. No hay disponibilidad que vigilar, ni almacenamiento que purgar, ni tokens de servidor que rotar del lado del servidor. Es el punto de partida correcto para la inmensa mayoría de proyectos; solo cuando aparecen razones de coste a gran escala, residencia de datos o entornos aislados tiene sentido plantearse alojarla tú, que es el tema del siguiente nivel.

Antes de seguir, conviene fijar qué cambia en tu día a día una vez enlazado: nada en tu forma de trabajar. Sigues ejecutando turbo run build igual que antes. Lo único distinto es que, por debajo, cada tarea consulta primero si su resultado ya existe en la caché compartida y solo compila si no. La caché remota es deliberadamente invisible cuando funciona; su presencia se nota únicamente en que las cosas tardan mucho menos de lo que deberían.

En CI: TURBO_TOKEN y TURBO_TEAM

CI no puede hacer turbo login: no hay navegador, no hay quien complete un flujo interactivo, y el runner es efímero. La conexión se establece, en cambio, por variables de entorno. TURBO_TOKEN es un token de acceso de Vercel que autentica al runner, y TURBO_TEAM es el identificador del equipo cuya caché se comparte. Con esas dos presentes, turbo se conecta a la caché remota sin ninguna interacción.

name: ci
on: push
jobs:
  build:
    runs-on: ubuntu-latest
    env:
      TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
      TURBO_TEAM: ${{ vars.TURBO_TEAM }}
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - run: pnpm install --frozen-lockfile
      - run: pnpm turbo run build test lint

El token va en los secretos del repositorio, nunca en el YAML ni en el control de versiones; el equipo puede ir en una variable normal porque no es secreto. Con esto, el runner efímero del nivel uno deja de nacer con la caché fría: aunque su disco esté vacío, ahora puede preguntarle a la caché remota si el trabajo ya existe, y para el noventa por ciento intacto la respuesta será que sí.

El token merece el mismo cuidado que cualquier credencial de producción, porque en la práctica lo es. Dale el alcance mínimo que necesite, guárdalo en el almacén de secretos de tu CI y no en el YAML, y rótalo con la cadencia con la que rotarías la llave de una base de datos. Un token de caché filtrado no es tan catastrófico como uno de despliegue, pero permite a un tercero leer los artefactos de tus builds y, peor, escribir en la caché que todo tu equipo consume.

💡
Verifica que el acierto es remoto, no local

Para comprobar que la caché remota funciona de verdad y no te engaña un acierto local, fuerza a Turborepo a ignorar la caché de disco y usar solo la remota. Ejecuta la misma tarea dos veces desde estados limpios distintos y busca en la salida el resumen: cuántas tareas fueron acierto y cuántas se ejecutaron. Un FULL TURBO con todo cacheado desde una máquina que nunca compiló esa tarea es la prueba de que el artefacto viajó por la red desde otra.

Conviene conocer dos variables más que rondan este mismo mecanismo. TURBO_API apunta al endpoint de la caché, y su valor por defecto es el de Vercel; cambiarlo es lo que permite apuntar a una caché self-hosted, como veremos. Y TURBO_REMOTE_CACHE_SIGNATURE_KEY activa la firma de artefactos: si la defines con la misma clave en todas las máquinas, cada artefacto se firma al subir y se verifica al bajar, de modo que un artefacto manipulado se rechaza antes de restaurarse.

Variable Papel Dónde vive
TURBO_TOKEN autentica al runner contra la caché secretos de CI
TURBO_TEAM identifica el equipo cuyo ámbito se comparte variable normal
TURBO_API endpoint de la caché, por defecto el de Vercel variable normal
TURBO_REMOTE_CACHE_SIGNATURE_KEY firma y verifica los artefactos secretos de CI

Blindar la caché compartida frente a PRs no confiables

Una caché remota es estado compartido y mutable, y todo estado compartido y mutable es una superficie de ataque. El escenario a temer es el PR desde un fork no confiable: si el pipeline de ese PR tiene un token de escritura, código malicioso en el PR podría subir un artefacto envenenado bajo el hash de una tarea legítima, y entonces cualquier máquina —CI de la rama principal incluida— que obtenga ese “acierto” restauraría la salida manipulada. Es una forma de cache poisoning, y no es hipotética: envenenamientos de caché de build documentados en 2025 mostraron exactamente esta clase de fallo.

La defensa es el mínimo privilegio sobre la caché. Para los contextos no confiables, el runner debe poder leer de la caché pero no escribir en ella. Turborepo permite ese modo de solo lectura, de forma que un PR de fork se beneficia de los aciertos existentes sin poder contaminar el almacén compartido.

# En jobs disparados por PRs de forks: leer si, escribir no
turbo run build --remote-cache-read-only

El principio que subyace es el de mínimo privilegio aplicado a un recurso compartido: cada consumidor de la caché recibe justo el acceso que su nivel de confianza justifica, y ni una pizca más. El CI de la rama principal, que corre código ya revisado y fusionado, puede escribir; el CI de un PR de un fork, que corre código de un desconocido, solo lee. Esta asimetría no es paranoia, sino el reconocimiento de que una caché compartida amplifica por igual el trabajo legítimo y el malicioso, y que la única frontera entre ambos es quién tiene permiso para escribir en ella.

Vale la pena nombrar por qué los forks son el caso peligroso y no los PRs internos. Un PR de un miembro del equipo corre código de alguien con acceso legítimo, cuya mala intención sería un problema mayor que la caché; un PR de un fork corre código de cualquier desconocido de internet que abrió un pull request. Darle a ese código permiso de escritura sobre la caché que tu producción consume es, literalmente, dejar que un extraño deposite artefactos que tu equipo restaurará. Solo lectura para forks no es paranoia, es la configuración correcta por defecto.

🔑

Token en secretos

TURBO_TOKEN vive en los secretos de CI, con el mínimo alcance necesario y rotación periódica. Nunca en el repositorio.

✍️

Firma de artefactos

TURBO_REMOTE_CACHE_SIGNATURE_KEY firma al subir y verifica al bajar. Un artefacto manipulado se rechaza antes de restaurarse.

🛡️

Solo lectura para forks

Los PRs no confiables leen la caché pero no escriben. Se benefician de los aciertos sin poder envenenar el almacén.

🔐

turbo login

Autentica tu CLI contra Vercel una vez, por navegador. Deja un token en la configuración global de tu usuario.

🔗

turbo link

Asocia el repo con la caché de un equipo. Escribe .turbo/config.json, sin secretos, comiteable.

🤖

TOKEN y TEAM

En CI, sin login interactivo, las dos variables conectan al runner efímero con la caché compartida.

Verificar y depurar la caché

Activar la caché es fácil; confiar en ella exige saber inspeccionarla. La primera herramienta es la corrida en seco, que muestra el plan —qué tareas correrían, con qué hash, con qué entradas— sin ejecutar nada. Es lo primero que hay que mirar cuando un acierto que esperabas no aparece, porque casi siempre revela la causa: un hash que difiere entre dos entornos que deberían coincidir.

turbo run build --dry=json    # el plan y los hashes, sin ejecutar
turbo run build --summarize   # un resumen de la ejecucion real

El síntoma clásico de un problema es “en mi máquina acierta pero en CI no”. La causa nunca es la red: es que el hash difiere, y difiere porque alguna entrada no está declarada o cambia entre los dos entornos —una variable presente en tu shell y ausente en CI, un archivo generado localmente que se cuela en los inputs, una versión de dependencia distinta—. Comparar los hashes de la corrida en seco en ambos entornos localiza la fuga en minutos, no en horas.

# Tras un cambio pequeno con la cache caliente
 Tasks:    18 successful, 18 total
Cached:    17 cached, 18 total   # 17 aciertos, solo 1 tarea corrio
  Time:    3.4s

El resumen de una ejecución te da el número que lo condensa todo: cuántas tareas acertaron y cuántas corrieron. Cuando todas aciertan aparece FULL TURBO, la señal de que ni una sola tarea se ejecutó. Convertir ese número en una métrica que vigilas a lo largo del tiempo es lo que transforma la caché de una comodidad ocasional en una garantía de la que dependes.

Un último hábito de verificación que ahorra sustos: revisa de vez en cuando que la caché no se esté escribiendo desde donde no debe. Un vistazo a quién ha subido artefactos recientemente, o una comprobación de que los jobs de PR corren en solo lectura, detecta a tiempo tanto un token demasiado amplio como una configuración que, sin querer, dejó a un contexto no confiable con permiso de escritura. La caché compartida es infraestructura, y la infraestructura se audita.

El token es la frontera de confianza alrededor de un cerebro compartido

Activar el remote caching es, en apariencia, un trámite de dos comandos, pero lo que enciendes por debajo es algo más ambicioso de lo que parece: conviertes a tu equipo y a tu CI en un único organismo que memoiza, un cerebro compartido donde el trabajo que hace cualquiera queda disponible para todos. Y en el momento en que muchas partes leen y escriben una memoria común, dos propiedades que en una caché local eran triviales se vuelven el problema central. La primera es la identidad: la caché tiene que saber a qué equipo pertenece cada artefacto, porque el hash direcciona por contenido pero el aislamiento entre organizaciones lo da el ámbito; ese es el papel de TURBO_TEAM. La segunda es la integridad y la autorización: quién puede leer, quién puede escribir, y cómo se verifica que lo que se restaura es lo que se subió; ese es el papel de TURBO_TOKEN, de la firma y del modo de solo lectura. Fíjate en el patrón, porque es una ley general de los sistemas: la corrección y la seguridad de un sistema distribuido no se reparten uniformemente, se concentran en su estado compartido. Una caché local no tiene fronteras de confianza porque no hay nadie más dentro; una caché remota es toda ella una frontera, y por eso las mismas dos variables que la hacen útil son las que la hacen segura o peligrosa según cómo las manejes. La destreza aquí no es teclear turbo link, que lo hace cualquiera; es entender que has creado un recurso compartido y mutable, y tratar su token con el mismo respeto con que tratarías la llave de una base de datos de producción: alcance mínimo, en secretos, rotado, y con escritura negada a todo el que no te fíes. Un cerebro compartido acelera a todo el equipo cuando se cuida, y le miente a todo el equipo a la vez cuando no.

⚔️ Enciende y asegura la caché compartida
  1. Ejecuta turbo login y turbo link en un repo de prueba y confirma que se creó .turbo/config.json con el equipo y la API, sin secretos dentro.
  2. Corre turbo run build dos veces y observa cómo la segunda es un acierto; luego bórralo del disco y comprueba que el acierto remoto lo recupera igual.
  3. Configura un job de CI con TURBO_TOKEN en secretos y TURBO_TEAM en variables, y verifica en el resumen que las tareas se resuelven por acierto remoto.
  4. Añade --remote-cache-read-only al job que corre en PRs de forks y explica qué ataque concreto previene.
  5. Activa TURBO_REMOTE_CACHE_SIGNATURE_KEY en tus máquinas y en CI, y comprueba que un artefacto sin firma válida es rechazado.