Sign commits published to GitLab

Published on September 3rd, 2026

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 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 <[email protected]>" rsa4096 sign never

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

bash
gpg --list-secret-keys --keyid-format=long [email protected]

Then export both halves:

bash
gpg --armor --export-secret-keys [email protected] > private.asc
gpg --armor --export [email protected] > 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 .

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:

KeySupported
RSA, no passphraseYes
RSA, passphrase protectedNo
ed25519, ECDSA and other ECCNo

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

Troubleshooting

SymptomCause
Signing commits is enabled but no signing key is providedGITLAB_COMMIT_SIGNING_KEY is not set, or the backend has not restarted since it was saved
The step fails while reading the keyThe 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 UnverifiedThe 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 reportedThe key could not be read at all. Check the task log for a message about the signing key
The namespace ... is not foundThe 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