Git CMS

Reference

The plutoGithub options, the environment variables, and the private key formats.

Options

Set the options under plutoGithub in nuxt.config.ts. Most sites set none. See Defaults.

OptionDefaultRule
enabled'auto''auto', true or false. 'auto' turns the CMS on unless a backend layer is present. See Git CMS.
storage'fs' in development, 'github' in production'github' or 'fs'. 'fs' is valid in development only.
github.repoFound by Pluto. See Defaults.owner/name. Required when storage is github.
github.branch'main'The branch to read and commit to.
github.appSlugThe env var NUXT_PLUTO_GITHUB_APP_SLUG or PLUTO_GITHUB_APP_SLUG, else ''The slug of the GitHub App.
github.apiUrl'https://api.github.com'The API base URL.
dir'content'A relative path. No .., no leading /, no drive letter. The module changes \ to / and removes a trailing /.
commit.localtrueIn development (storage: 'fs'), commits each save to the local Git repository of the project. Set it to false to only write the files.
commit.skipDeploytrueAdds a [skip ci] marker to the commit messages. A site that must redeploy on each edit, such as a docs site, sets it to false.
cache.headTtl60Seconds. Zero or more.
cache.storage'cache'The Nitro storage mount for the cache.
postTypetrueRegisters the fixed post content type, on the server and on the client. With false, the admin does not show Posts and does not register the post capabilities. A layer that brings its own types sets it to false.

A bad option turns the CMS off and logs an error. The module does not throw.

The cache time of the public answers is runtimeConfig.plutoApi.maxAge of core. See Public API.

Defaults

An explicit option always wins over a default.

Storage. In development, the storage is fs. In production, it is github. The env var PLUTO_GITHUB_STORAGE=github makes a development server use GitHub.

Repository. When github.repo is not set, Pluto looks in this order and uses the first match:

  1. The env var NUXT_PLUTO_GITHUB_REPO, then PLUTO_GITHUB_REPO.
  2. The Vercel variables VERCEL_GIT_REPO_OWNER and VERCEL_GIT_REPO_SLUG.
  3. In development only, the origin remote of the local git repository. Pluto reads .git/config and accepts the https and ssh URLs of github.com. It runs no shell command.

Missing repository. In production, with the storage github and no repository, the module logs one error and the CMS stays off. The build does not fail. Set PLUTO_GITHUB_REPO, or set plutoGithub.github.repo. A nuxt prepare run stays quiet.

A site on Vercel, or a site that runs the dev server in its own git clone, needs no plutoGithub option. A build on another host sets PLUTO_GITHUB_REPO.

Environment variables

The secrets are run-time variables. The module does not check them at build time. Set the secrets as NUXT_PLUTO_GITHUB_<NAME>.

The setup wizard writes these values for you. See Quickstart. To fill them by hand, see GitHub App.

VariableRequiredValue
PLUTO_GITHUB_REPONoowner/name of the repository. Pluto finds it in most cases. See Defaults.
PLUTO_GITHUB_APP_SLUGNoThe URL name of the GitHub App (github.com/apps/<slug>).
PLUTO_GITHUB_STORAGENoSet to github to test against GitHub in development.
NUXT_PLUTO_GITHUB_SESSION_PASSWORDYes32 characters or more, random.
NUXT_PLUTO_GITHUB_APP_IDYesThe App ID.
NUXT_PLUTO_GITHUB_CLIENT_IDYesThe Client ID.
NUXT_PLUTO_GITHUB_CLIENT_SECRETYesThe client secret.
NUXT_PLUTO_GITHUB_PRIVATE_KEYYesThe private key of the App. See Private key formats.
NUXT_PLUTO_GITHUB_READ_TOKENNoA read-only token. It replaces the installation token for anonymous reads.
NUXT_PLUTO_GITHUB_ALLOWED_USERSNoGitHub logins, comma-separated, without case. When set, only these users can sign in, even with push access.

The module puts a non-secret copy of enabled, repo, branch, appSlug, storage, dir, postType, and skipDeploy in runtimeConfig.public.plutoGithub.

Old names

Before version 0.28, the variables had CMS names, such as NUXT_PLUTO_CMS_GITHUB_APP_ID and PLUTO_CMS_REPO. Pluto 0.29 does not read them. Rename them in your host and in .env: NUXT_PLUTO_CMS_GITHUB_<NAME> and NUXT_PLUTO_CMS_<NAME> become NUXT_PLUTO_GITHUB_<NAME>, and PLUTO_CMS_<NAME> becomes PLUTO_GITHUB_<NAME>.

Private key formats

The variable NUXT_PLUTO_GITHUB_PRIVATE_KEY accepts three formats:

  • A PEM with real line breaks.
  • A PEM on one line, with a literal \n for each line break.
  • The base64 of the PEM, on one line. The value has no -----BEGIN text. Pluto decodes it and uses the result when it holds a PEM.

The setup wizard writes the base64 format. One line is easy to paste in a host dashboard.

To make the base64 value of a .pem file yourself, run base64 -w0 key.pem.

A minimal config

With the defaults, a site sets only the options that differ:

nuxt.config.ts
export default defineNuxtConfig({
  extends: ['@plutocms/core'],
  plutoGithub: {
    github: { branch: 'content' }, // only when the branch is not main
    dir: 'docs-content' // only when the folder is not content
  }
})

Develop with local Git

In development, the CMS uses storage: 'fs' by default. The dev server reads and writes the local content/ folder, and commits each save to the local Git repository of your project. It never pushes: you push when you are ready.

  • One save is one commit, with a message such as content(post): create "hello". The commit holds only the files of that save. Changes that you staged in other files stay staged.
  • The commit uses your Git name and email, and runs the hooks of your repository.
  • create-pluto makes the new project a Git repository with a first commit, so this works on the first save.
  • Without a Git repository, or without Git, Pluto only writes the files and logs a warning. Run git init to commit each save.
  • If a commit fails (for example, a hook stops it), the save is still on disk. The server log says why.
  • A local commit has no [skip ci] marker, so your push deploys as usual.
  • To only write the files, set commit.local: false.

Local Git publishing works in development only. storage: 'fs' is an error in a build. On a deployed site, the admin saves only when a GitHub App is connected. See Quickstart.

Open http://localhost:3000/admin. On your machine, you are signed in as a fixed local user with all capabilities. No GitHub step is needed.

The fs store uses the project directory as the root, so a post is at <project>/content/posts/<slug>.md.

The dev local user works only for a request from your machine. A request with x-forwarded-for or forwarded is not local. A request whose Host is not localhost, 127.0.0.1, or [::1] is not local either. A tunnel or a DNS-rebinding page gets no access.

Deploy

Deploy to a host with a server runtime, such as Vercel, Netlify, or Node. The site reads the posts at run time, so a static export does not work. See Deploy to Vercel.

Commits

Commit messages have this form. <type> is the name of the content type. For the built-in type, it is post.

  • content(<type>): create "slug"
  • content(<type>): update "slug"
  • content(<type>): publish "slug" and unpublish
  • content(<type>): rename "a" -> "b"
  • content(<type>): delete "slug"

A save that changes nothing makes no commit.

With commit.skipDeploy on, a [skip ci] marker ends the message. A host that honors the marker does not rebuild on each save. Not every host reads this marker. Test it on your host. With skipDeploy on, the edit form shows no build status.

Cache

The store keeps these in a cache:

  • The head of the branch, for cache.headTtl seconds.
  • The directory index, by <dir>@<headOid>.
  • The tree index of a nested type, by head.
  • The parsed files, by blob id.
  • The rendered HTML and the excerpts, by blob id.

A blob id never changes, so a blob entry never goes stale. The default backend is a Map. With cache.storage, the cache uses a Nitro storage mount.

GitHub Enterprise Server

Set plutoGithub.github.apiUrl to https://<host>/api/v3. The OAuth pages use https://<host>. Any other API URL uses https://github.com.

Rate limits

When GitHub rate limits a call, the CMS answers 503 with a Retry-After header.