Skip to content

Deploying

Deploying an Alinea site means deploying your Next.js app. In production the dashboard is served by your site on /admin, and your handler route saves the editors' changes as commits to your git repository. To do that it needs a backend that signs users in and talks to your repository: Alinea Cloud or your own.

Prepare your project

Config

Tell Alinea where your site lives, where the handler is and on which path the dashboard is served, in your CMS config:

cms.ts
import {createCMS} from 'alinea/next'

export const cms = createCMS({
  // schema and workspaces ...
  baseUrl: {
    development: 'http://localhost:3000',
    production: 'https://example.com'
  },
  handlerUrl: '/api/cms',
  adminPath: '/admin'
})

baseUrl.production is required: the handler URL is resolved against it. Your config is also bundled into the dashboard, where only environment variables starting with NEXT_PUBLIC_ or PUBLIC_ exist. Use one of those if you read the URL from the environment, for example production: process.env.NEXT_PUBLIC_SITE_URL.

Build scripts

Run Next.js through the Alinea CLI. alinea build bundles your content, generates the dashboard and then runs the command after --:

package.json
{
  "scripts": {
    "dev": "alinea dev -- next dev",
    "build": "alinea build -- next build",
    "start": "next start"
  }
}

Wrap your Next.js config in withAlinea from alinea/next. It serves the dashboard on adminPath, routes media URLs (/admin/file/...) to your handler and allows them in next/image:

next.config.ts
import {withAlinea} from 'alinea/next'

export default withAlinea({
  // your Next.js config
})

The build writes the dashboard to your public folder: an admin.html file and an admin folder with its assets, named after adminPath. They are generated on every build, so leave them out of git (alinea init adds these lines to .gitignore):

.gitignore
/public/admin.html
/public/admin/

The handler route

The route you created with alinea init handles every request of the dashboard:

app/(alinea)/api/cms/route.ts
import {cms} from '@/cms'
import {createHandler} from 'alinea/next'

const handler = createHandler({cms})

export const GET = handler
export const POST = handler

Without a backend option the handler connects to Alinea Cloud. Pass backend to host it yourself. Use the afterCommit hook to revalidate pages after editors publish, see Instant publishing.

Commit hooks

Two hooks on the handler run for every commit, whatever it contains. beforeCommit receives the changes before they are committed: inspect or rewrite them and return the mutations to commit, or return nothing to keep them as they are. afterCommit runs once the commit is made, with the mutations and the sha of the new commit: revalidate pages, notify your team or trigger a deploy.

app/(alinea)/api/cms/route.ts
import {cms} from '@/cms'
import {createHandler} from 'alinea/next'
import {revalidatePath} from 'next/cache'

const handler = createHandler({
  cms,
  beforeCommit({mutations}) {
    // Inspect or rewrite the changes, return the mutations to commit
    return mutations
  },
  afterCommit({sha}) {
    revalidatePath('/', 'layout')
  }
})

export const GET = handler
export const POST = handler

Both hooks can be async. Errors thrown in afterCommit are logged and don't fail the commit.

Hosting requirements

  • Node.js 24 or higher where your site runs, not only where it builds: the bundled content database is opened with Node's built-in SQLite.

  • The Node.js runtime for the handler route. The handler refuses to run on the Edge runtime.

  • Server rendering. The handler is an API route, so a static export (output: 'export') can't run the dashboard or serve media.

Deploy

Push your code and deploy it as usual. Then open /admin on your deployed site:

  • Without a backend, the dashboard walks you through connecting the site to Alinea Cloud.

  • With a self-hosted backend, editors sign in with the method you configured.

After the first publish, check that the change shows up on your site and that a commit appeared in your repository.

In this section