# Sign commits published to GitLab

> Sign the commits made by the publish:gitlab action with a PGP key so GitLab marks them as Verified.

*Published: 2026-09-03*


## Actions used

- `fetch:template`
- `publish:gitlab`

For more information on these actions and others visit `/create/actions` in your Roadie application.

## Overview

By default the commits that `publish:gitlab` makes are unsigned. You can have
them signed with a PGP key by storing the private key in Roadie as a secret and
setting `signCommit: true` on the action.

Once GitLab can match the signature to a key on a user's profile, it shows the
commit as Verified.

## Generate a signing key

The key must be **RSA** and must have **no passphrase**. Other key types are not
supported — see [Supported keys](#supported-keys) below. Note that recent
versions of `gpg` create an ed25519 key unless you ask for RSA explicitly:

```bash
gpg --batch --passphrase '' --quick-generate-key "Scaffolder <scaffolder@example.com>" rsa4096 sign never
```

Check what you got. You are looking for `rsa4096` on the `sec` line:

```bash
gpg --list-secret-keys --keyid-format=long scaffolder@example.com
```

Then export both halves:

```bash
gpg --armor --export-secret-keys scaffolder@example.com > private.asc
gpg --armor --export scaffolder@example.com > public.asc
```

## Add the private key to Roadie

Go to `https://<tenant-name>.roadie.so/administration/settings/secret` and paste
the contents of `private.asc` into `GITLAB_COMMIT_SIGNING_KEY`.

The value spans multiple lines. The field expands into a text area when you
paste a multi-line value, and the line breaks matter — a key whose line breaks
have been lost cannot be read. Paste the whole block, including the
`-----BEGIN PGP PRIVATE KEY BLOCK-----` and `-----END PGP PRIVATE KEY BLOCK-----`
lines.

Choose **Save and Restart**. The key is not picked up until the backend has
restarted.

If your GitLab integration was configured before commit signing was available,
open your GitLab settings in Roadie and save them once. This refreshes the
stored integration configuration so it includes the signing key.

## Add the public key to GitLab

In GitLab, go to **Preferences → GPG Keys** and add the contents of
`public.asc`.

Two things have to line up or GitLab will show the commit as Unverified even
though the signature itself is good:

- the email you pass as `gitAuthorEmail` must be one of the user IDs on the key
- that email must be added and verified on the GitLab account

## Template

You can create a template with this content or import
[from here](https://github.com/RoadieHQ/software-templates/blob/main/scaffolder-templates/gitlab-signed-commit/template.yaml).

```yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: gitlab-signed-commit
  title: GitLab repo with a PGP signed commit
  description: Publishes a repo to GitLab and signs the initial commit.

spec:
  owner: group:default/engineering
  type: service

  parameters:
    - title: Where to publish
      required:
        - repoUrl
      properties:
        repoUrl:
          title: Repository location
          type: string
          ui:field: RepoUrlPicker
          ui:options:
            allowedHosts:
              - gitlab.com

    - title: Commit identity
      required:
        - gitAuthorName
        - gitAuthorEmail
      properties:
        gitAuthorName:
          title: Commit author name
          type: string
          default: Roadie Scaffolder
        gitAuthorEmail:
          title: Commit author email
          type: string
          description: Must match a user ID on the signing key

  steps:
    - id: fetch-skeleton
      name: Fetch skeleton
      action: fetch:template
      input:
        url: ./skeleton
        values:
          gitAuthorName: ${{ parameters.gitAuthorName }}
          gitAuthorEmail: ${{ parameters.gitAuthorEmail }}

    - id: publish
      name: Publish to GitLab
      action: publish:gitlab
      input:
        repoUrl: ${{ parameters.repoUrl }}
        repoVisibility: private
        defaultBranch: main
        gitCommitMessage: Initial commit from the Roadie scaffolder
        gitAuthorName: ${{ parameters.gitAuthorName }}
        gitAuthorEmail: ${{ parameters.gitAuthorEmail }}
        signCommit: true

  output:
    links:
      - title: Repository
        url: ${{ steps['publish'].output.remoteUrl }}
      - title: Signed commit
        url: ${{ steps['publish'].output.remoteUrl }}/-/commit/${{ steps['publish'].output.commitHash }}
```

In the **Owner** field of the repository picker, use the GitLab namespace path —
your username or a group path, such as `my-group`. It is not your display name.

## Check the result

Open the commit in GitLab and look for the Verified badge. You can also check
the signature from a clone:

```bash
git log --show-signature -1
```

`gpg: Good signature` means the commit was signed with your key.

## Supported keys

Commit signing is done in the scaffolder itself rather than by shelling out to
`gpg`, and the library used supports a narrow set of keys:

| Key                          | Supported |
| ---------------------------- | --------- |
| RSA, no passphrase           | Yes       |
| RSA, passphrase protected    | No        |
| ed25519, ECDSA and other ECC | No        |

An unsupported key is only detected when a template runs, so it is worth
confirming the key type when you generate it.

## Troubleshooting

| Symptom                                                                     | Cause                                                                                                         |
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `Signing commits is enabled but no signing key is provided`                 | `GITLAB_COMMIT_SIGNING_KEY` is not set, or the backend has not restarted since it was saved                   |
| The step fails while reading the key                                        | The key is not RSA, is passphrase protected, or lost its line breaks when it was pasted                       |
| `git log --show-signature` shows a good signature but GitLab says Unverified | The public key is not on the GitLab profile, or `gitAuthorEmail` is not a verified email and a user ID on the key |
| Commits are unsigned and no error is reported                               | The key could not be read at all. Check the task log for a message about the signing key                      |
| `The namespace ... is not found`                                            | The **Owner** field holds a display name rather than a GitLab namespace path                                  |

A failed signing step happens *after* the GitLab project has been created, so a
failed run can leave an empty project behind. Delete it before retrying with the
same name.

## Further Reading

- [`publish:gitlab` action reference](/backstage/scaffolder-actions/?availability=roadie)
- [GitLab documentation on signing commits with GPG](https://docs.gitlab.com/user/project/repository/signed_commits/gpg/)
