Custom storage adapter
Store artifacts anywhere Files SDK has an adapter, by returning it from apps/api/src/custom-adapter.ts.
When apps/api/src/custom-adapter.ts returns a Files SDK adapter, the server uses it for every request, ahead of the bindings and the S3_* variables.
The hook
The file exports one function. It receives the platform environment and returns an adapter, or undefined to keep the built-in selection:
import type { CustomAdapter } from './utils/storage';
export const customAdapter: CustomAdapter = () => undefined;
env holds the Worker’s variables, secrets, and bindings on Cloudflare, and process.env on other platforms. The server calls the function on every request, so read configuration from env inside it, not at import time.
The server bundles everything that custom-adapter.ts imports. Import only the adapter you use.
Check what the adapter supports
Not every Files SDK adapter can store artifacts. The server needs three things from it:
- User metadata. The server stores the task’s duration and the artifact’s signature as metadata. An adapter that rejects metadata, such as Vercel Blob, fails every upload.
lastModifiedinlist(). The expiry task deletes every artifact whose age it can’t read. With an adapter that returns nolastModified, such as Netlify Blobs, every expiry run deletes the whole cache.- Your platform’s runtime. Many adapters use Node.js SDKs that don’t run in Workers. Check the adapter’s page before you deploy it to Cloudflare.
Store artifacts on a local disk
This example uses the Files SDK fs adapter on one Node.js server with a persistent disk. Files SDK describes fs as a development adapter, because nothing replicates the files.
-
Return the adapter when
CACHE_DIRis set:import { fs } from 'files-sdk/fs'; import type { CustomAdapter } from './utils/storage'; export const customAdapter: CustomAdapter = (env) => typeof env.CACHE_DIR === 'string' ? fs({ root: env.CACHE_DIR }) : undefined;files-sdkis already a dependency of the server, andfsneeds no other package. -
Build the server for Node.js:
NITRO_PRESET=node_server pnpm build -
Start it with a directory for the artifacts:
cd apps/api CACHE_DIR=/var/lib/turborepo-cache TURBO_TOKEN=<your token> PORT=3000 pnpm start -
Set
TURBO_API=http://<server>:3000and run a build twice, as in Connect turbo. The second run printscache hit, replaying logs.
The server writes each artifact to <CACHE_DIR>/<team>/<hash>, with its metadata in a <hash>.meta.json file beside it.
Store artifacts in Google Cloud Storage
The GCS adapter needs Google’s SDK. Add it to the server:
pnpm --filter @turborepo-remote-cache/api add @google-cloud/storage
Then return the adapter:
import { gcs } from 'files-sdk/gcs';
import type { CustomAdapter } from './utils/storage';
export const customAdapter: CustomAdapter = (env) =>
typeof env.GCS_BUCKET === 'string' ? gcs({ bucket: env.GCS_BUCKET }) : undefined;
The SDK finds credentials through Google’s Application Default Credentials.