Developer Tools AWS

Turborepo Remote Cache on AWS User Guide

| Product: Turborepo Remote Cache

Overview

This guide covers the deployment and operation of a self-hosted Turborepo Remote Cache on AWS using cloudimg AWS Marketplace images. Turborepo Remote Cache is a fast server that implements Vercel's Turborepo Remote Cache API, so your build and CI machines share the results of already-completed tasks instead of recomputing them.

The image installs the official turborepo-remote-cache npm package (pinned at build) globally on Node.js 22 LTS and runs it under systemd as the unprivileged turbo user, so a production-ready cache is serving within minutes of launch.

Secure by design - never an open cache. The cache server itself binds to loopback 127.0.0.1:3000 only and is never exposed to the network. An nginx front end on port 80 exposes a public, unauthenticated health endpoint for load balancers and proxies the token-gated cache API (/v8/...) to the server, which enforces bearer-token authentication. There is no default token inside the image: a unique bearer token is generated on this instance's first boot and written to a root-only file, and never baked into the image. Unauthenticated requests to the cache API are rejected (HTTP 400 for a missing header, HTTP 401 for a bad token), so only the per-instance token can read or write cache data. A fail-secure bootstrap gate means the cache server cannot start at all until that per-instance token exists, so the image can never come up as an open cache.

Local storage. Cache artifacts are stored on a local filesystem storage provider at /var/lib/turborepo-remote-cache/cache on the instance disk, with no external cloud dependency.

What is included:

  • Turborepo Remote Cache 2.11.2 (official npm package, pinned at build), run under systemd as the unprivileged turbo user (turborepo-remote-cache.service)

  • An nginx front end (nginx.service) on port 80 with a public, unauthenticated health endpoint and a token-gated proxy to the cache API

  • A per-instance bearer token generated on first boot, enforced on the cache API, and never baked into the image

  • A fail-secure bootstrap gate so the cache never starts without a per-instance token

  • Unattended security upgrades left enabled so the server keeps receiving patches

Cache service active, loopback-only listener

Prerequisites

  • Active AWS account, an EC2 key pair, a VPC and subnet in the target region

  • Subscription to the Turborepo Remote Cache listing on AWS Marketplace

  • A security group allowing TCP 22 (admin) and, from the build machines and CI runners that will use the cache, TCP 80 (cache API and health)

Recommended instance type: m5.large (2 vCPU, 8 GB RAM). Light cache use runs comfortably; busy caches shared across large teams or CI fleets can move to a larger instance type and a larger root volume.

Step 1: Launch from AWS Marketplace

Find Turborepo Remote Cache on AWS Marketplace, published by cloudimg, and choose Continue to Subscribe, then Continue to Configuration and Continue to Launch. Select the Ubuntu 24.04 delivery option, your instance type (m5.large), your key pair, and a security group that allows TCP 22 for administration plus TCP 80 from the client networks that will use the cache.

Step 2: Connecting to your instance

SSH in with your EC2 key pair as the login user for the OS variant you launched.

OS variant SSH login user Example
Ubuntu 24.04 ubuntu ssh -i your-key.pem ubuntu@<public-ip>
ssh -i your-key.pem ubuntu@<public-ip>

Step 3: First boot

On first boot the image resolves the instance public IP via EC2 instance metadata (IMDSv2), mints a per-instance bearer token, writes the fail-secure bootstrap marker, and then systemd starts the cache server. This completes within a minute.

Confirm both services are active, and that the cache server is bound to loopback only while nginx serves the network:

sudo systemctl start turborepo-remote-cache.service nginx.service 2>/dev/null || true
sleep 2
sudo systemctl is-active turborepo-remote-cache.service nginx.service
sudo ss -tln | grep -E ':(80|3000) '

The cache and nginx services report active, the cache server listens on 127.0.0.1:3000 (loopback only), and nginx listens on port 80.

The unauthenticated health endpoint is handled entirely by nginx (no cache round-trip), which makes it ideal for a load-balancer health check:

curl -s http://127.0.0.1/
curl -s -o /dev/null -w 'health: HTTP %{http_code}\n' http://127.0.0.1/

Turborepo Remote Cache version and public health

Step 4: Retrieve your cache token

The per-instance bearer token lives in a root-only file. Read it over SSH:

sudo cat /root/turborepo-remote-cache-info.txt

The file holds the TURBO_TOKEN for this instance, the cache API URL (TURBO_API), and the team id (TURBO_TEAM, cloudimg). These values are unique to this instance and were generated on first boot - they are never baked into the image.

Step 5: Authentication is enforced

The cache is never an open target. The command below reads the token straight from the root-only file, so no secret is printed. An authenticated request to the cache API returns HTTP 200, an anonymous request is rejected with HTTP 400 (missing header), and a wrong-token request is rejected with HTTP 401:

sudo systemctl start turborepo-remote-cache.service nginx.service 2>/dev/null || true
sleep 2
T=$(sudo grep '^TURBO_TOKEN=' /root/turborepo-remote-cache-info.txt | cut -d= -f2-)
PH=$(head -c 64 /dev/urandom | sha256sum | awk '{print $1}')
curl -s -o /dev/null -w 'authenticated status:     HTTP %{http_code}\n' -H "Authorization: Bearer $T" http://127.0.0.1/v8/artifacts/status
curl -s -o /dev/null -w 'anonymous artifact GET:   HTTP %{http_code}\n' "http://127.0.0.1/v8/artifacts/$PH?teamId=cloudimg"
curl -s -o /dev/null -w 'wrong-token artifact GET: HTTP %{http_code}\n' -H "Authorization: Bearer wrongtoken" "http://127.0.0.1/v8/artifacts/$PH?teamId=cloudimg"

The authenticated request prints 200, the anonymous request prints 400, and the wrong-token request prints 401.

Bearer-token authentication enforced

Step 6: Store and retrieve a cache artifact

The Turborepo Remote Cache API is a content-addressed artifact store: a task output is stored with a PUT under its hash and retrieved with a GET. The block below performs a full authenticated round-trip - store an artifact, retrieve it byte-for-byte, and confirm an anonymous request for the same artifact is rejected. The token is read from the root-only file, so no secret is printed:

sudo systemctl start turborepo-remote-cache.service nginx.service 2>/dev/null || true
sleep 2
T=$(sudo grep '^TURBO_TOKEN=' /root/turborepo-remote-cache-info.txt | cut -d= -f2-)
BLOB=$(head -c 512 /dev/urandom | base64 -w0)
H=$(printf '%s' "$BLOB" | sha256sum | awk '{print $1}')
PUT=$(printf '%s' "$BLOB" | curl -s -o /dev/null -w '%{http_code}' -H "Authorization: Bearer $T" -H 'Content-Type: application/octet-stream' -X PUT --data-binary @- "http://127.0.0.1/v8/artifacts/$H?teamId=cloudimg")
echo "authenticated PUT  artifact ${H:0:12}   HTTP $PUT"
GOT=$(curl -s -H "Authorization: Bearer $T" "http://127.0.0.1/v8/artifacts/$H?teamId=cloudimg")
[ "$GOT" = "$BLOB" ] && echo "authenticated GET  artifact ${H:0:12}   byte-for-byte match OK" || echo "MISMATCH"
UNG=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1/v8/artifacts/$H?teamId=cloudimg")
echo "anonymous GET      artifact ${H:0:12}   HTTP $UNG  (rejected)"

The PUT returns 200, the authenticated GET returns the artifact byte-for-byte, and the anonymous GET is rejected - a full content-addressed round-trip through the token-gated cache API.

Authenticated artifact store and retrieve round-trip

Step 7: Point Turborepo at your cache

From a client or CI runner, point Turborepo at your cache by setting the API URL, team and token. Replace <public-ip> with your instance's public IP and <token> with the TURBO_TOKEN value from Step 4:

export TURBO_API=http://<public-ip>
export TURBO_TOKEN=<token>
export TURBO_TEAM=cloudimg
turbo build --remote-cache-timeout 60

On the first build Turborepo uploads task outputs to the cache; on subsequent builds - locally or in CI, on any machine that shares this configuration - Turborepo downloads the cached result instead of recomputing it. You can verify connectivity from a client with a single authenticated request:

curl -H "Authorization: Bearer <token>" http://<public-ip>/v8/artifacts/status

Step 8: Storage and configuration notes

  • Cache storage. Cache artifacts are stored on the local filesystem at /var/lib/turborepo-remote-cache/cache. Confirm free space with df -h /.

  • Non-secret configuration lives at /etc/turborepo-remote-cache/turbo-cache.env (the systemd EnvironmentFile): AUTH_MODE=static, STORAGE_PROVIDER=local, the STORAGE_PATH, the loopback HOST/PORT, and the body-size limit. The per-instance secret TURBO_TOKEN lives separately in /etc/turborepo-remote-cache/token.env (0600 root:root), read by systemd at start.

  • nginx front end is at /etc/nginx/sites-available/turborepo-remote-cache. The public / and /health endpoints are unauthenticated liveness probes; /v8/... is proxied to the cache server, which enforces the bearer token.

  • Per-instance secrets. The token copy in /root/turborepo-remote-cache-info.txt is 0600 root:root. The token is generated on first boot and never baked into the image; a fail-secure gate stops the cache starting until it exists.

  • Production TLS. For production, front the cache with your own domain and a CA-signed TLS certificate (for example an nginx :443 server block, or an AWS load balancer terminating TLS) so the bearer token is never sent in clear text over the network.

Step 9: Managing the services

sudo systemctl --no-pager --lines=3 status turborepo-remote-cache.service nginx.service | cat

Restart the cache with sudo systemctl restart turborepo-remote-cache, and reload nginx after a configuration change with sudo systemctl reload nginx. Both services are enabled at boot.

Support

This image is published and supported by cloudimg. For deployment help, configuration questions, or issues, contact support@cloudimg.co.uk. Every cloudimg image ships with 24/7 support and fully patched operating systems with unattended security upgrades enabled.

This is a repackaged open source software product with additional charges for cloudimg support services. Turborepo Remote Cache is distributed under the MIT license. cloudimg is not affiliated with or endorsed by Vercel, Inc. Turborepo and Vercel are trademarks of their respective owners. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.