{
  "id": "osc-stack-migrate-nextjs-from-frontend-cloud",
  "job": "Move a Next.js or other Node.js app, with its Postgres data, Redis-style key-value data and blobs, off a proprietary frontend cloud and onto open source services, then switch traffic.",
  "aliases": [
    "migrate next.js app off frontend cloud",
    "move next.js app to open source hosting",
    "self-host next.js app with postgres redis s3"
  ],
  "inputs": [
    {
      "protocol": "git",
      "source": "the app's git repository (GitHub or any HTTPS git host); append #branch for a non-default branch; private repositories use a pre-registered git credential"
    },
    {
      "protocol": "postgres",
      "source": "a pg_dump of the current database"
    },
    {
      "protocol": "S3",
      "source": "the current blob store, read with any S3 client"
    }
  ],
  "outputs": [
    {
      "protocol": "HTTPS",
      "consumer": "the app's users, on the app's OSC address first and on your own domain after the switch",
      "runtimes": ["nodejs"]
    }
  ],
  "handsOffTo": {
    "id": "osc-stack-deploy-agent-written-app",
    "descriptorUrl": "https://www.osaas.io/stacks/deploy-agent-written-app.json",
    "why": "Steps 1, 3, 5 and 6 below are the same chain as that recipe (PostgreSQL, MinIO, parameter store, create-my-app). Follow its notes for the parameter store's one-time CONFIG_API_KEY and for wait-for-app-ready. This recipe adds Valkey, the data move and the traffic switch."
  },
  "recommendedPath": {
    "chain": [
      {
        "step": 1,
        "serviceId": "birme-osc-postgresql",
        "role": "relational database replacing the marketplace Postgres",
        "requiredConfig": ["name", "PostgresPassword"],
        "requiresCredential": true
      },
      {
        "step": 2,
        "serviceId": "valkey-io-valkey",
        "role": "Redis-compatible key-value store",
        "requiredConfig": ["name"],
        "requiresCredential": false,
        "note": "Optional. Password is optional and sensitive (live schema 2026-10-15). Reachability from a My App was not exercised in this recipe's own run."
      },
      {
        "step": 3,
        "serviceId": "minio-minio",
        "role": "S3-compatible blob storage",
        "requiredConfig": ["name"],
        "requiresCredential": false,
        "note": "Optional. Copy blobs with any S3 client pointed at the instance endpoint."
      },
      {
        "step": 4,
        "action": "move data",
        "role": "pg_dump from the old database, pg_restore into step 1; copy blobs into step 3",
        "note": "Run before the switch; re-run a final delta or freeze writes during the cutover window."
      },
      {
        "step": 5,
        "mcpTool": "setup-parameter-store",
        "role": "environment variables, including the new connection strings",
        "requiredConfig": ["name", "enableSecrets"],
        "requiresCredential": false,
        "note": "See handsOffTo for the one-time CONFIG_API_KEY handling."
      },
      {
        "step": 6,
        "mcpTool": "create-my-app",
        "role": "build and run the app from git",
        "requiredConfig": ["name", "type"],
        "requiresCredential": false,
        "note": "type nodejs. The runner runs npm install (devDependencies included), npm run build, npm start, and injects PORT=8080, APP_URL and AUTH_URL. No Dockerfile or config file needed for a stock create-next-app project (measured 2026-10-14). Then wait-for-app-ready."
      },
      {
        "step": 7,
        "action": "switch traffic",
        "role": "point your domain at the app, keep the old deployment until traffic has moved, then remove it"
      }
    ],
    "tokensPerDay": null,
    "tokensPerDayMeasured": false,
    "measuredWallClockSeconds": null,
    "wallClockMeasured": false,
    "gapNote": "Only step 6 has been run end to end (stock create-next-app, Next.js 16.4.0, React 19.3.0, OSC-managed Gitea source, 2026-10-14). The data steps and Valkey were not part of that run. A rebuild after a new commit was live within a couple of minutes; no exact figure is recorded, so none is given."
  },
  "doesNotMove": [
    "edge-runtime functions and middleware: the app runs as a standard Node.js server",
    "vendor storage SDKs: swap for pg, a Redis client and an S3 client",
    "automatic per-pull-request preview deployments: no equivalent verified; a branch can run as its own My App via #branch"
  ],
  "notVerified": [
    "middleware",
    "next/image optimisation",
    "route handlers under /api",
    "ISR revalidation",
    "custom domain",
    "NEXT_PUBLIC_* values from a parameter store at build time",
    "a GitHub source (the test used OSC-managed Gitea)",
    "Next.js majors older than 16"
  ],
  "servicesVerifiedLive": {
    "date": "2026-10-15",
    "method": "osc_call_tool wrap (name=get-service-schema) on valkey-io-valkey; list-service-categories. Runtime facts from a live create-my-app test deploy in the author's workspace on 2026-10-14 (get-my-app-logs plus HTTP checks: GET / 200 with x-powered-by Next.js, a per-request server-rendered route returned different timestamps two seconds apart). The app was deleted afterwards.",
    "catalogTotal": 198,
    "catalogTotalSource": "list-service-categories via osc_call_tool on 2026-10-15, per-category sum 10+27+13+31+66+25+19+7 = 198"
  },
  "gateCheck": {
    "gatedServiceId": "eyevinn-just-go-live",
    "usesGatedService": false,
    "note": "This stack does not name or link eyevinn-just-go-live."
  },
  "verifiedOn": "2026-10-15",
  "publishedAt": "https://www.osaas.io/migrate/from-frontend-cloud",
  "descriptorUrl": "https://www.osaas.io/stacks/migrate-nextjs-from-frontend-cloud.json"
}
