---
url: /en/docs/backends.md
description: >-
  Configure supported Git backends in Sveltia CMS with setup options and best
  practices.
---

# Backends

A backend defines where content is stored and how Sveltia CMS interacts with it. Sveltia CMS primarily supports Git-based backends, allowing seamless integration with popular Git hosting services.

## Supported Backends

Sveltia CMS supports the following Git-based backends:

* [GitHub](/en/docs/backends/github)
* [GitLab](/en/docs/backends/gitlab)
* [Gitea/Forgejo](/en/docs/backends/gitea-forgejo)

For testing purposes, you can also use the [Test Backend](/en/docs/backends/test).

Some features only work with specific backends.

::: warning Breaking changes from Netlify/Decap CMS

For performance reasons, Sveltia CMS does not support the **Azure DevOps**, **Bitbucket** and **Git Gateway** backends. Please note that [Git Gateway](https://docs.netlify.com/manage/security/secure-access-to-sites/git-gateway/) has officially been deprecated by Netlify. If you’re using one of these backends with Netlify/Decap CMS, consider switching to GitHub, GitLab, Gitea or Forgejo before migrating to Sveltia CMS.

Also, Sveltia CMS does not support the undocumented custom backend API. The `CMS.registerBackend` method is a noop in Sveltia CMS. We may add support for custom backends in future releases, though compatibility with existing Netlify/Decap CMS custom backends is not guaranteed.

:::

## Configuration

All the configuration options for backends can be set in the `backend` option of the CMS configuration file. Here is a basic example of configuring the GitHub backend:

::: code-group

```yaml [YAML]
backend:
  name: github
  repo: user/repo
```

```toml [TOML]
[backend]
name = "github"
repo = "user/repo"
```

```json [JSON]
{
  "backend": {
    "name": "github",
    "repo": "user/repo"
  }
}
```

```js [JavaScript]
{
  backend: {
    name: "github",
    repo: "user/repo",
  },
}
```

:::

See the specific backend guides for detailed configuration instructions.

The following sections describe some common configuration options available for all Git-based backends.

### Branch Selection

By default, Sveltia CMS interacts with the repository’s default branch (usually `main` or `master`). You can specify a different branch using the `branch` option in the backend configuration:

::: code-group

```yaml [YAML]{4}
backend:
  name: github
  repo: user/repo
  branch: develop
```

```toml [TOML]{4}
[backend]
name = "github"
repo = "user/repo"
branch = "develop"
```

```json [JSON]{5}
{
  "backend": {
    "name": "github",
    "repo": "user/repo",
    "branch": "develop"
  }
}
```

```js [JavaScript]{5}
{
  backend: {
    name: "github",
    repo: "user/repo",
    branch: "develop",
  },
}
```

:::

### Monorepos

If your repository holds more than one site, or the site lives in a subdirectory along with other code, set the `root_dir` option to the directory of the site. Sveltia CMS then treats that directory as the root of the repository: every path in the configuration, such as a collection’s `folder` or the `media_folder`, is relative to it, and nothing outside it shows up in the CMS. The option is available for all the Git-based backends and the [Test Backend](/en/docs/backends/test).

For example, with a site in `apps/blog`, a collection whose files live in `apps/blog/content/posts` is configured with `folder: content/posts`:

::: code-group

```yaml [YAML]{4}
backend:
  name: github
  repo: user/repo
  root_dir: apps/blog
collections:
  - name: posts
    folder: content/posts
```

```toml [TOML]{4}
[backend]
name = "github"
repo = "user/repo"
root_dir = "apps/blog"

[[collections]]
name = "posts"
folder = "content/posts"
```

```json [JSON]{5}
{
  "backend": {
    "name": "github",
    "repo": "user/repo",
    "root_dir": "apps/blog"
  },
  "collections": [{ "name": "posts", "folder": "content/posts" }]
}
```

```js [JavaScript]{5}
{
  backend: {
    name: "github",
    repo: "user/repo",
    root_dir: "apps/blog",
  },
  collections: [{ name: "posts", folder: "content/posts" }],
}
```

:::

The path is relative to the repository root, and it can’t climb out of the repository with `..`. Likewise, the other paths in the configuration can’t lead outside the directory with `..`, as the CMS doesn’t list the files there. If the directory doesn’t exist on the branch, the CMS says so after you sign in. With [Editorial Workflow](/en/docs/workflows/editorial), the path also becomes part of branch names, so no part of it can start with a dot, end with `.lock` or contain a space or any of `~^:?*[\` or `@{`, which Git doesn’t allow in a branch name.

Each site in the monorepo can have its own CMS with its own `root_dir`, and they don’t get in each other’s way:

* Only the files in the directory are listed when the CMS loads, which saves listing the whole of a big monorepo.
* A commit made to another part of the repository doesn’t make the CMS reload its content: only the directory is checked for changes. The [deployment status](/en/docs/deployments) also stays on the last commit that changed the directory, as a build for such a commit is usually skipped.
* With [Editorial Workflow](/en/docs/workflows/editorial), the directory is part of the branch name, e.g. `cms/apps/blog/posts/hello-world`, so two sites with a collection of the same name don’t share branches or list each other’s unpublished entries. An entry saved before you set the option keeps its branch and stays on the board, as long as all its files are in the directory. An entry whose pull request also changes a file outside the directory can’t be published from the CMS, as the change can’t be shown.
* Each site gets a local cache of its own in the browser.

Commits are still made to the repository as a whole: the `{{path}}` [template tag](#available-template-tags) in commit messages is the path from the repository root, e.g. `apps/blog/content/posts/hello-world.md`. To tell the sites apart in the Git history, you can also put the site name in the [commit messages](#commit-messages).

With the [local workflow](/en/docs/workflows/local), select the root directory of the repository, not the directory of the site, when the CMS asks for it. The CMS finds the site’s directory within it.

If the sites are deployed separately, for example as several Netlify sites built from the same repository, set the [`preview_context`](/en/docs/workflows/deploy-previews#specifying-a-status-context) option so the CMS links to the right [deploy preview](/en/docs/workflows/deploy-previews). A build that was canceled or skipped because the commit didn’t touch the site is ignored either way.

::: warning

The `root_dir` option only limits what the CMS shows and edits. Users still need write access to the whole repository, and the Git hosting service doesn’t stop them from changing other parts of it. To restrict access to a site, keep it in a repository of its own.

:::

### Authentication Methods

By default, Sveltia CMS allows users to sign in using either OAuth or an access token. You can restrict the available sign-in methods by setting the `auth_methods` option to an array containing only the methods to allow:

| Value   | Description                                |
| ------- | ------------------------------------------ |
| `oauth` | OAuth sign-in (e.g. “Sign In with GitHub”) |
| `token` | Access token sign-in                       |

For example, to allow only OAuth sign-in and disable access token authentication:

::: code-group

```yaml [YAML]{4}
backend:
  name: github
  repo: user/repo
  auth_methods: [oauth]
```

```toml [TOML]{4}
[backend]
name = "github"
repo = "user/repo"
auth_methods = ["oauth"]
```

```json [JSON]{5}
{
  "backend": {
    "name": "github",
    "repo": "user/repo",
    "auth_methods": ["oauth"]
  }
}
```

```js [JavaScript]{5}
{
  backend: {
    name: "github",
    repo: "user/repo",
    auth_methods: ["oauth"],
  },
}
```

:::

To allow only access token sign-in and disable OAuth:

::: code-group

```yaml [YAML]{4}
backend:
  name: github
  repo: user/repo
  auth_methods: [token]
```

```toml [TOML]{4}
[backend]
name = "github"
repo = "user/repo"
auth_methods = ["token"]
```

```json [JSON]{5}
{
  "backend": {
    "name": "github",
    "repo": "user/repo",
    "auth_methods": ["token"]
  }
}
```

```js [JavaScript]{5}
{
  backend: {
    name: "github",
    repo: "user/repo",
    auth_methods: ["token"],
  },
}
```

:::

The `auth_methods` array must contain at least one method. An empty array will result in a configuration error.

### OAuth Endpoint

When a user signs in with OAuth, Sveltia CMS opens an authorization URL made of the `base_url` and `auth_endpoint` options joined with a slash. Leading and trailing slashes are ignored. The token URL is derived from the same URL by replacing `/authorize` with the backend’s token path. The defaults are:

| Backend       | `base_url`                | `auth_endpoint`         |
| ------------- | ------------------------- | ----------------------- |
| GitHub        | `https://api.netlify.com` | `auth`                  |
| GitLab        | `https://api.netlify.com` | `auth`                  |
| Gitea/Forgejo | `https://gitea.com`       | `login/oauth/authorize` |

With [PKCE authorization](/en/docs/backends/gitlab#pkce-authorization) (`auth_type: pkce`), the GitLab defaults are `https://gitlab.com` and `oauth/authorize` instead, because the CMS talks to GitLab directly.

You usually only need to change `base_url`. Set `auth_endpoint` if your OAuth client or Git service serves the authorization page at a different path:

::: code-group

```yaml [YAML]{4-5}
backend:
  name: gitlab
  repo: owner/repo
  base_url: https://auth.example.com
  auth_endpoint: gitlab/oauth/authorize
```

```toml [TOML]{4-5}
[backend]
name = "gitlab"
repo = "owner/repo"
base_url = "https://auth.example.com"
auth_endpoint = "gitlab/oauth/authorize"
```

```json [JSON]{5-6}
{
  "backend": {
    "name": "gitlab",
    "repo": "owner/repo",
    "base_url": "https://auth.example.com",
    "auth_endpoint": "gitlab/oauth/authorize"
  }
}
```

```js [JavaScript]{5-6}
{
  backend: {
    name: "gitlab",
    repo: "owner/repo",
    base_url: "https://auth.example.com",
    auth_endpoint: "gitlab/oauth/authorize",
  },
}
```

:::

::: warning Breaking change from Netlify/Decap CMS

The `auth_endpoint` option must be a path relative to `base_url`, not a full URL. The Decap CMS documentation shows a full URL like `https://gitea.example.com/login/oauth/authorize` for the Gitea backend, which results in an invalid authorization URL in Sveltia CMS. Put the origin in `base_url` and the path in `auth_endpoint` instead, or omit `auth_endpoint` to use the default path.

:::

### Site Domain

With the [authorization code flow](/en/docs/backends/github#authorization-code-flow) on GitHub and GitLab, Sveltia CMS sends the site’s domain to the OAuth client as the `site_id` query parameter. Netlify uses it to find the site that holds the OAuth app credentials, and [Sveltia CMS Authenticator](https://github.com/sveltia/sveltia-cms-auth) can check it against its list of allowed domains. PKCE authorization and access token sign-in don’t use it.

By default, the domain is the current hostname, or `cms.netlify.com` if the CMS is running on `localhost`. To send a different domain, for example when the CMS is served from a preview URL that isn’t registered with the OAuth client, set the `site_domain` option:

::: code-group

```yaml [YAML]{4}
backend:
  name: github
  repo: user/repo
  site_domain: www.example.com
```

```toml [TOML]{4}
[backend]
name = "github"
repo = "user/repo"
site_domain = "www.example.com"
```

```json [JSON]{5}
{
  "backend": {
    "name": "github",
    "repo": "user/repo",
    "site_domain": "www.example.com"
  }
}
```

```js [JavaScript]{5}
{
  backend: {
    name: "github",
    repo: "user/repo",
    site_domain: "www.example.com",
  },
}
```

:::

When Netlify is the OAuth client, an internationalized domain name is converted to Punycode before it’s sent.

### Commit Messages

You can customize the Git commit messages used when saving content. The `commit_messages` option allows you to define templates for various actions. Here’s the default configuration:

::: code-group

```yaml [YAML]
backend:
  commit_messages:
    create: 'Create {{collection}} "{{slug}}"'
    update: 'Update {{collection}} "{{slug}}"'
    delete: 'Delete {{collection}} "{{slug}}"'
    uploadMedia: 'Upload "{{path}}"'
    deleteMedia: 'Delete "{{path}}"'
    openAuthoring: '{{message}}'
```

```toml [TOML]
[backend.commit_messages]
create = "Create {{collection}} \"{{slug}}\""
update = "Update {{collection}} \"{{slug}}\""
delete = "Delete {{collection}} \"{{slug}}\""
uploadMedia = "Upload \"{{path}}\""
deleteMedia = "Delete \"{{path}}\""
openAuthoring = "{{message}}"
```

```json [JSON]
{
  "backend": {
    "commit_messages": {
      "create": "Create {{collection}} \"{{slug}}\"",
      "update": "Update {{collection}} \"{{slug}}\"",
      "delete": "Delete {{collection}} \"{{slug}}\"",
      "uploadMedia": "Upload \"{{path}}\"",
      "deleteMedia": "Delete \"{{path}}\"",
      "openAuthoring": "{{message}}"
    }
  }
}
```

```js [JavaScript]
{
  backend: {
    commit_messages: {
      create: 'Create {{collection}} "{{slug}}"',
      update: 'Update {{collection}} "{{slug}}"',
      delete: 'Delete {{collection}} "{{slug}}"',
      uploadMedia: 'Upload "{{path}}"',
      deleteMedia: 'Delete "{{path}}"',
      openAuthoring: '{{message}}',
    },
  },
}
```

:::

The available commit types are:

* `create`, `update`, `delete`: Used when creating, updating, or deleting entries in collections.
* `uploadMedia`, `deleteMedia`: Used when uploading or deleting media assets.
* `openAuthoring`: Wraps the message generated by one of the types above when the commit is made by an [Open Authoring](/en/docs/workflows/open) contributor, so you can record who wrote it. `{{message}}` is that generated message. The default template is `{{message}}` on its own, which changes nothing.

::: tip

Unlike most of other config options, the commit message keys are camelCased.

:::

#### Available Template Tags

You can use the following template tags in commit messages:

* `{{collection}}`: The `label_singular` or `label` of the collection.
* `{{slug}}`: The slug of the entry.
* `{{path}}`: The file path of the entry or media asset, relative to the repository root, even with the [`root_dir`](#monorepos) option.
* `{{message}}`: The commit message generated for the change, wrapped by the `openAuthoring` template.
* `{{author-email}}`: The email of the signed-in user, if available.
* `{{author-login}}`: The login name of the signed-in user, if available.
* `{{author-name}}`: The display name of the signed-in user, if available.

The following table summarizes which tags are supported for each commit type:

| Commit Type | Supported Tags |
| --- | --- |
| `create`, `update`, `delete` | `collection`, `slug`, `path`, `author-email`, `author-login`, `author-name` |
| `uploadMedia`, `deleteMedia` | `path`, `author-email`, `author-login`, `author-name` |
| `openAuthoring` | `message`, `author-email`, `author-login`, `author-name` |

#### Skipping CI/CD

It’s also possible to add the `[skip ci]` prefix to commit messages to prevent triggering CI/CD pipelines. See the [deployments guide](/en/docs/deployments) for more details.

::: info Future Plans

We plan to add an option that prompts users to enter custom commit messages in the UI before saving changes.

:::

### Including Credentials in API Requests

By default, Sveltia CMS does not include cookies in API requests to the Git hosting service. If your self-hosted Git service instance requires authentication via cookies, you can set the `include_credentials` option to `true`:

::: code-group

```yaml [YAML]{4}
backend:
  name: gitea
  repo: user/repo
  include_credentials: true
```

```toml [TOML]{4}
[backend]
name = "gitea"
repo = "user/repo"
include_credentials = true
```

```json [JSON]{5}
{
  "backend": {
    "name": "gitea",
    "repo": "user/repo",
    "include_credentials": true
  }
}
```

```js [JavaScript]{5}
{
  backend: {
    name: "gitea",
    repo: "user/repo",
    include_credentials: true,
  },
}
```

:::

Your server must also set the [`Access-Control-Allow-Credentials`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Credentials) header in API responses for this to work.
