All guides

Self-hosted Quickstart

Install Airdraft in your own Next.js project in under 5 minutes using the CLI.

3 min read432 words

Self-hosting Airdraft means running the CMS backend inside your own application. Your content, your server, your database — zero dependency on Airdraft's cloud infrastructure.

Using Airdraft Cloud instead? See Getting Started with Airdraft Cloud.

Prerequisites

  • Node.js 18+
  • A Next.js 14+ project (App Router recommended)
  • npm, pnpm, or yarn

Step 1 — Run the initialiser

In your project root:

npx airdraft init

The CLI walks you through a short setup wizard:

  1. Mode — choose Own app (auto) to scaffold everything automatically, or Own app (manual) if you prefer to wire things yourself.
  2. Database — choose a storage backend:
    • Local files (default, zero config — MDX/JSON on disk, great for development)
    • SQLite — single-file database, ideal for VPS deployments
    • PostgreSQL — production-ready, requires a connection string
    • MongoDB — production-ready, requires a connection string
  3. Plugins — select capabilities to add (authentication, media, SEO, audit log, schema editor).
  4. Blog scaffold — optionally generate a posts collection and sample blog pages.

The CLI writes:

File What it does
airdraft.config.ts Main config — adapter, plugins, schema path
airdraft.schema.json Collection definitions
app/api/cms/[...cms]/route.ts Next.js catch-all API route
instrumentation.ts Database migration on server start
.env.local Environment variable placeholders

Step 2 — Fill in environment variables

Open .env.local and set the required values:

# Generate with: npx airdraft generate-secret
AIRDRAFT_JWT_SECRET=your-secret-here

# Your CMS API URL (same origin in development)
NEXT_PUBLIC_CMS_API_URL=http://localhost:3000/api/cms

# For SQLite:
DATABASE_URL=.airdraft.db

# For PostgreSQL:
# DATABASE_URL=postgresql://user:password@localhost:5432/mydb

# For MongoDB:
# MONGODB_URI=mongodb://localhost:27017/mydb

Generate a secure JWT secret:

npx airdraft generate-secret

Step 3 — Understand the generated config

The CLI produces a config like this:

// airdraft.config.ts
import { defineConfig, LocalAdapter } from '@airdraft/core'
import { withAutoAuth } from '@airdraft/plugin-auth'
import { withAutoMedia } from '@airdraft/plugin-media'
import { withSeo } from '@airdraft/plugin-seo'
import { withSchemaEditor } from '@airdraft/plugin-schema-editor'
import { withAuditLog } from '@airdraft/plugin-audit-log'

const adapter = new LocalAdapter({ root: './content' })

const media = await withAutoMedia({ storageAdapter: adapter })
const auth = withAutoAuth()  // returns null if AIRDRAFT_JWT_SECRET not set

export default defineConfig({
  adapter,
  schemaPath: 'airdraft.schema.json',
  plugins: [
    withAuditLog({ logFilePath: './logs/audit.log' }),
    media,
    withSeo(),
    withSchemaEditor({ adminKey: process.env.CMS_ADMIN_KEY }),
    ...(auth ? [auth] : []),
  ],
})

The withAutoMedia call requires { storageAdapter: adapter } — this is the content adapter used to store media sidecar metadata alongside files.

Step 4 — Start the dev server

npm run dev

Your CMS API is live at http://localhost:3000/api/cms. Visit http://localhost:3000/api/cms/docs for the auto-generated API reference.

Step 5 — Create the first admin user

npx airdraft create-user

The CLI prompts for email, password, and role. Choose admin for the first user.

For a database-backed adapter, the generated startup migration creates the user and invite indexes and create-user writes through adapter.userStore(). The default .airdraft/users.json store is appropriate only for local development or servers with persistent disks; use a database-backed user store on serverless or horizontally scaled deployments.

Step 6 — Open the admin UI

If you selected the Schema editor plugin, mount CMSAdmin from @airdraft/react-ui:

// app/admin/[[...segments]]/page.tsx
import { CMSAdmin } from '@airdraft/react-ui'
import '@airdraft/react-ui/styles.css'

export default function AdminPage() {
  return (
    <CMSAdmin
      basePath="/admin"
      apiUrl={process.env.NEXT_PUBLIC_CMS_API_URL!}
    />
  )
}

Step 7 — Query your content

Use createCmsClient from @airdraft/next in Server Components — it calls the engine directly, no HTTP:

// lib/cms.ts
import { createCmsClient } from '@airdraft/next'
import airdraft from '@/airdraft.config'

export const cms = createCmsClient(airdraft)
const { entries } = await cms.listEntries('posts', { status: 'published' })

Manual setup (advanced)

If you prefer to wire things manually, install the packages directly:

npm install @airdraft/next @airdraft/core @airdraft/plugin-auth @airdraft/plugin-media
// app/api/cms/[...cms]/route.ts
import { createCmsHandler } from '@airdraft/next'
import airdraft from '@/airdraft.config'

export const { GET, POST, PATCH, PUT, DELETE } = createCmsHandler(airdraft)

See Using Airdraft in Next.js for the complete setup.

Resources

What's next