Migrate from Vercel

Vercel is opinionated around the Next.js shape. Joule Cloud doesn't have a Next-specific runtime — we run any container — but the deploy ergonomics are similar and the Next app keeps working. The trade: less Vercel-specific magic; more control over where it runs and what it costs.

The mental model

VercelJoule Cloud
Vercel Functions / Edge FunctionsFunctions
Vercel Compute / fluid computeCompute
Vercel Storage (Blob)Object Store
Vercel Postgres (Neon)JouleDB
Vercel KV (Upstash)JouleDB indexed key-value
Edge ConfigObject Store + cached read
Vercel AI SDKOpenAI-shape baseURL swap — see Node SDK

Deploying a Next.js app

Vercel auto-builds your Next app behind the scenes. We need a Dockerfile (Next has supported standalone output for a while):

Dockerfile

FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci

FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:22-alpine AS runner
WORKDIR /app
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]

invisible.hcl

workload "site" {
  image  = "ghcr.io/me/mysite:1.0"
  region = "auto"
  cpu    = "1 vCPU"
  memory = "1 GB"
  scale {
    min = 0      # scale to zero like Vercel
    max = 30
    on_metric = "requests_per_sec > 50"
  }
}

route "mysite.com" {
  to = workload.site
  https = true
}

Vercel AI SDK keeps working

import { createOpenAI } from "@ai-sdk/openai";
import { generateText } from "ai";

const jc = createOpenAI({
  baseURL: "https://api.greenjoules.cloud/v1",
  apiKey: process.env.JC_API_KEY,
});

const { text } = await generateText({
  model: jc("auto"),
  prompt: "Hi",
});

What you lose

What you gain