Deployment
Cloudflare Workers with Sandbox
Run Repix on Cloudflare Workers using the Sandbox SDK for isolated container execution.
Cloudflare Sandbox SDK lets you run containers from Workers. Since Repix uses Sharp (native Node.js + libvips), it requires a container environment. The Sandbox SDK provides this.
Workers Paid plan required. The Sandbox SDK is available on Cloudflare's Workers Paid plan. A custom domain with wildcard DNS is required for preview URLs in production (e.g.,
*.images.yourdomain.com).Architecture
- A Worker receives image transformation requests
- The Worker gets a Sandbox container running Repix
- Repix runs inside the Sandbox with full Sharp/libvips support
- The Worker proxies requests to the Sandbox and returns the response
Setup
1. Install Dependencies
npm install @cloudflare/sandbox wrangler
2. Sandbox Dockerfile for Repix
Create a Dockerfile that packages Repix for the Sandbox. The Sandbox base includes Node.js; we add Sharp dependencies:
# Dockerfile.sandbox
FROM node:22-alpine
# Sharp dependencies
RUN apk add --no-cache vips-dev build-base python3 make g++ pkgconfig
WORKDIR /app
# Copy repix
COPY package*.json ./
COPY tsconfig.json ./
COPY vite.config.ts ./
RUN npm ci --include=dev
COPY src/ ./src/
RUN npm run build && npm ci --only=production --omit=dev
EXPOSE 3210
CMD ["node", "dist/index.js"]
Alternatively, use the existing Repix Dockerfile if it's compatible with Sandbox. The key is that the image must run Repix on port 3210 and expose it.
3. Wrangler Configuration
Create wrangler.jsonc in a new worker directory (or at repo root if this is a Worker-only project):
{
"name": "repix-worker",
"main": "src/index.ts",
"compatibility_date": "2024-01-01",
"containers": [
{
"class_name": "Sandbox",
"image": "../Dockerfile.sandbox",
"instance_type": "lite",
"max_instances": 5,
},
],
"durable_objects": {
"bindings": [{ "class_name": "Sandbox", "name": "Sandbox" }],
},
"migrations": [{ "new_sqlite_classes": ["Sandbox"], "tag": "v1" }],
}
4. Worker Code
// src/index.ts
import { getSandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";
export default {
async fetch(request: Request, env: { Sandbox: DurableObjectNamespace }) {
const url = new URL(request.url);
// Forward API routes (/, /health, /presets) and image requests (/:transform/*)
const pathSegments = url.pathname.split("/").filter(Boolean);
const isApiRoute =
url.pathname === "/" ||
url.pathname === "/health" ||
url.pathname === "/presets";
const isImageRequest = pathSegments.length >= 2;
if (!isApiRoute && !isImageRequest) {
return new Response(
JSON.stringify({
service: "Repix",
endpoint: "/{preset|params}/{source-url}",
}),
{ headers: { "Content-Type": "application/json" } },
);
}
const sandbox = getSandbox(env.Sandbox, "repix");
// Expose Repix's port - returns URL to the running service
const { url: sandboxUrl } = await sandbox.exposePort(3210);
// Forward request to Repix running in the Sandbox
const repixUrl = `${sandboxUrl}${url.pathname}${url.search}`;
const response = await fetch(repixUrl, {
method: request.method,
headers: request.headers,
body: request.body,
});
return new Response(response.body, {
status: response.status,
headers: response.headers,
});
},
};
5. Deploy
cd worker
npx wrangler deploy
Important Notes
Cold Starts
Sandbox containers start on first use. The container sleeps after ~10 minutes of inactivity. Expect 5–15 second cold starts for the first request after sleep.
Preview URLs
Production preview URLs require a custom domain with wildcard DNS:
- Add domain:
images.yourdomain.comwith*.images.yourdomain.com - The Sandbox preview URL will use this for serving
Cost
Sandbox usage is billed separately. Monitor usage in the Cloudflare dashboard.

