Skip to main content

Deploying a Next.js app to Tencent Cloud CloudBase, step by step

CloudBase TeamCloudBase Team
10 min read

You have a Next.js project running fine locally, and you want to put it online for others to use.

Vercel? Unreliable access from China. Buy your own server? Then you're stuck with Nginx, PM2, SSL and the whole ops rigmarole.

There's actually another path — Tencent Cloud CloudBase's HTTP cloud functions. No server management, no Docker; push the code and it runs. SSR and API Routes are supported, and binding a domain gives you a production environment.

This post walks you from create-next-app to a live, accessible site. The whole thing takes about 15 minutes.

Before you start, prepare these

  • Node.js 18+ (20 recommended)
  • A Tencent Cloud account, with CloudBase cloud development enabled
  • Your CloudBase environment ID (visible on the console home page)

If you haven't installed the CloudBase CLI, you'll need it later for CI/CD:

npm install -g @cloudbase/cli

One more thing — if you use AI editors like Cursor, VS Code or Claude Code, install CloudBase MCP. The deploy step can then be done right in the editor, no console switching.

Step 1: create the Next.js project

Skip this if you already have a project.

npx create-next-app@latest my-cloudbase-app

Options are up to you, but make sure it uses the App Router (the default).

Enter the project directory and run it to confirm everything works:

cd my-cloudbase-app
npm run dev

Open http://localhost:3000 in a browser — seeing the Next.js welcome page is enough.

Step 2: edit next.config.js

This step is key. CloudBase HTTP cloud functions need Next.js to output standalone-mode artifacts, so it can run independently of node_modules.

Open next.config.mjs (or next.config.js, depending on your project) and change it to:

/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
images: {
unoptimized: true,
},
compress: true,
poweredByHeader: false,
}

export default nextConfig

Why each:

  • output: 'standalone' — generates an independently runnable build, not dependent on the full node_modules. This is the prerequisite for deploying to a cloud function.
  • images.unoptimized: true — the cloud function environment has no Sharp for image optimization; leaving it on causes build errors or runtime problems.
  • compress: true — enables gzip, reducing transfer size.
  • poweredByHeader: false — removes the X-Powered-By header; no need to expose it.

Step 3: write the scf_bootstrap startup file

An HTTP cloud function needs a file called scf_bootstrap (no extension), placed in the project root. It tells the cloud function how to start your app.

Create it in the project root:

touch scf_bootstrap
chmod +x scf_bootstrap

Contents:

#!/bin/bash

# CloudBase HTTP cloud functions listen on port 9000
export PORT=9000
export NODE_ENV=production

# start in standalone mode
node .next/standalone/server.js

A few notes:

  1. The port must be 9000 — a hard requirement of CloudBase HTTP cloud functions; change it and it won't run.
  2. The startup command is node .next/standalone/server.js, because with output: 'standalone', the build artifacts live in .next/standalone/. This minimizes package size and cold-start time. The official docs example uses npm start (i.e. next start), which also runs but needs the full node_modules, making it much larger.
  3. Windows users, take special note: this file must use LF line endings (Unix format), not CRLF. In VS Code, switch it at the bottom-right. Wrong line endings cause an exec format error after deploy — many people get stuck here. Add scf_bootstrap binary to .gitattributes to prevent Git from auto-converting line endings.

Step 4: build the project

npm run build

After building, check that .next/standalone/ exists and contains server.js.

You also need to copy the static assets over (standalone mode doesn't include them automatically):

cp -r public .next/standalone/public
cp -r .next/static .next/standalone/.next/static

Your project structure now looks roughly like:

my-cloudbase-app/
├── .next/
│ └── standalone/
│ ├── server.js ← entry
│ ├── public/ ← just copied
│ ├── .next/static/ ← just copied
│ └── ...
├── scf_bootstrap ← startup script
├── next.config.mjs
├── package.json
└── ...

Step 5: deploy to CloudBase

Two ways — pick whichever suits you.

If your editor has CloudBase MCP installed, you can deploy right in the editor. MCP creates the HTTP cloud function, uploads the code and configures the access route, all in one go.

In the AI editor, have the assistant do something like:

"Create an HTTP cloud function called nextjs-app, runtime Node.js 18.15, and deploy the current project to it"

What MCP does behind the scenes:

  1. Calls createFunction to create the HTTP cloud function (type: "HTTP", runtime: "Nodejs18.15")
  2. Uploads the project code (functionRootPath points to the parent of the function directory)
  3. Calls createFunctionHTTPAccess to configure the HTTP access route

After a successful deploy, you get a default access URL:

https://{your-env-id}.{region}.app.tcloudbase.com/nextjs-app

Method two: CloudBase CLI

# login (first time only)
tcb login

# create the HTTP cloud function and deploy
tcb fn deploy nextjs-app --path . --override

# create the HTTP access route
tcb service create -f nextjs-app -p /nextjs-app

After deploy, you can access it via the default domain too.

Verify

Open that URL — see your Next.js page? If so, congrats, the core part is done.

If you hit a 502 or a blank page, check:

  • Is the port in scf_bootstrap set to 9000
  • Are scf_bootstrap's line endings LF
  • Is the .next/standalone/ directory complete (has server.js, public/, .next/static/)

Step 6: bind a custom domain

The default app.tcloudbase.com domain works, but production needs your own domain.

Prerequisites:

  • The domain has completed ICP filing (mandatory in mainland China)
  • You have an SSL certificate for the domain (Tencent Cloud can issue one for free)

Flow:

  1. Go to the CloudBase console → HTTP access service
  2. Click "Add custom domain"
  3. Enter the domain, select the SSL certificate
  4. Create a "domain-associated resource", select your HTTP cloud function nextjs-app, set the trigger path to /
  5. In your domain's DNS management backend, add a CNAME record pointing to the CNAME value the console gives you

Wait for DNS to take effect (usually minutes), then visit your domain to see the app.

With the CLI, you can also do:

# add a custom domain (need the cert ID first)
tcb domains add your-domain.com --certid <cert-id> -e <env-id>

# configure the route
tcb routes set your-domain.com / --target nextjs-app --type function

Step 7: environment variables

Next.js has two kinds of environment variables:

  • NEXT_PUBLIC_* — injected at build time, accessible on the client
  • others — server-only (like database connection strings, API keys)

Set server env vars in the CloudBase console:

  1. Enter your cloud function → Function config → Environment variables
  2. Add key-value pairs, e.g. DATABASE_URL=mysql://...

With MCP too:

"Add an environment variable DATABASE_URL to the nextjs-app cloud function, value mysql://..."

With CLI:

tcb fn config update nextjs-app --envVariables '{"DATABASE_URL":"mysql://..."}'

One pitfall: updating env vars via MCP or API overwrites old values. If the function already has other variables, query first and merge, or you'll lose them.

As for NEXT_PUBLIC_* variables — they're decided at build time, not set in cloud function env vars. Export them before the build command, or write them in .env.production.

Step 8: CI/CD automation

Manual deploy after every change is tiring. Automate with GitHub Actions.

Create .github/workflows/deploy.yml in the project root:

name: Deploy to CloudBase

on:
push:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Build
run: npm run build

- name: Copy static assets
run: |
cp -r public .next/standalone/public
cp -r .next/static .next/standalone/.next/static

- name: Install CloudBase CLI
run: npm install -g @cloudbase/cli

- name: Login to CloudBase
run: tcb login --apiKeyId ${{ secrets.TCB_SECRET_ID }} --apiKey ${{ secrets.TCB_SECRET_KEY }}

- name: Deploy
run: |
tcb fn deploy nextjs-app --path . --override -e ${{ secrets.TCB_ENV_ID }}

Add three variables in your GitHub repo's Settings → Secrets:

  • TCB_SECRET_ID — Tencent Cloud API SecretId
  • TCB_SECRET_KEY — Tencent Cloud API SecretKey
  • TCB_ENV_ID — CloudBase environment ID

Now every push to main auto-builds and deploys.

Pitfall notes

Summing up the common pitfalls:

scf_bootstrap line endings Files created on Windows default to CRLF, causing exec format error after deploy. Fix: switch to LF in VS Code's bottom-right, or run dos2unix scf_bootstrap.

Runtime can't be changed A cloud function's Node.js runtime (e.g. 18.15) can't be changed after creation. To upgrade to 20.19 you must delete and recreate. Decide the version upfront.

Image optimization must be off Without images.unoptimized: true, Next.js tries to use Sharp for image optimization — a binary dependency the cloud function environment lacks, causing an immediate error.

Env var overwriting As above, updating env vars via API does a full overwrite. If your function has other variables, query them first and merge.

Cold start The first request, or when the function hasn't been called for a while, incurs cold-start latency. Next.js projects are large, so cold start can be a few seconds. If that's sensitive for your scenario, consider a scheduled trigger to keep it warm.

The dev experience with CloudBase MCP/Skills

A final note on AI-assisted development.

If you use an MCP-capable AI editor (Cursor, Claude Code, VS Code + Cline, etc.), install CloudBase MCP to connect your CloudBase environment.

Once installed, the AI assistant can directly:

  • Create and deploy cloud functions
  • View function logs and config
  • Manage environment variables
  • Configure domains and routes

Combined with CloudBase Skills (install: npx skills add tencentcloudbase/cloudbase-skills), AI also understands CloudBase best practices — like when to use an HTTP cloud function vs Cloud Run — without you digging through docs.

Simply put: MCP handles "what it can do" (permissions and connections), Skills handle "how it should be done" (norms and intuition). Together, the dev experience is noticeably smoother.

Summary

Recap the flow:

  1. create-next-app to create the project
  2. next.config.js with output: 'standalone'
  3. Write scf_bootstrap, listen on port 9000
  4. npm run build + copy static assets
  5. Deploy to a CloudBase HTTP cloud function via MCP or CLI
  6. Bind a custom domain
  7. Configure env vars
  8. GitHub Actions for CI/CD

HTTP cloud functions beat Cloud Run (containers) on startup speed, config lightness and cost. For most Next.js projects, this path is enough.

If you got it running, or got stuck somewhere, drop a comment.


Reference links:

Build your next app on CloudBase

An all-in-one backend covering database, cloud functions, static hosting, and AI capabilities.