Docs / CMS & Website Platforms / Set Up Keystone.js as a Headless CMS

Set Up Keystone.js as a Headless CMS

By Admin · Mar 15, 2026 · Updated Apr 23, 2026 · 139 views · 2 min read

Keystone.js is a powerful, developer-friendly headless CMS and application framework built on Node.js. It generates a GraphQL API and admin UI from your schema definition, making it ideal for building content-managed websites and applications. This guide covers deploying Keystone.js on your VPS.

Why Keystone.js?

  • Schema-driven: Define your data model in TypeScript, get a GraphQL API and admin UI automatically
  • Powerful relationships: First-class support for complex data relationships
  • Document field: Rich text editing with customizable blocks
  • Access control: Fine-grained, field-level access control
  • Extensible: Custom hooks, virtual fields, and server-side logic

Installation

# Prerequisites
sudo apt install -y postgresql nodejs npm

# Create project
cd /opt
npm init keystone-app@latest my-keystone-cms
cd my-keystone-cms

# Configure database connection
cat > .env  `/images${path}`,
      serverRoute: { path: '/images' },
      storagePath: 'public/images',
    },
  },
})

Querying with GraphQL

# Fetch published posts
query {
  posts(where: { status: { equals: "published" } }, orderBy: { publishedAt: desc }) {
    id
    title
    slug
    content { document }
    featuredImage { url width height }
    author { name }
    tags { name }
    publishedAt
  }
}

# Fetch single post
query GetPost($slug: String!) {
  post(where: { slug: $slug }) {
    title
    content { document }
    author { name bio }
    publishedAt
  }
}

Build and Deploy

# Build for production
npm run build

# Create systemd service
sudo cat > /etc/systemd/system/keystone.service  {
        if (session) return {}  // Staff sees all
        return { status: { equals: 'published' } }  // Public only sees published
      },
    },
  },
  // ... fields
})

Best Practices

  • Use database migrations: Run keystone prisma migrate for schema changes in production
  • Implement proper access control before going live — don't use allowAll in production
  • Set up a CDN for image delivery from the local storage
  • Use hooks for automated slug generation, validation, and side effects
  • Enable GraphQL playground only in development — disable in production

Was this article helpful?