---
layout: docs
page_title: Sync secrets from Vault to GitHub
description: >-
  Automatically sync and unsync the secrets from Vault to GitHub to centralize visibility and control of secrets lifecycle management.
---

# Sync secrets from Vault to GitHub

The GitHub actions sync destination allows Vault to safely synchronize secrets as GitHub organization, repository, or environment secrets.
This is a low footprint option that enables your applications to benefit from Vault-managed secrets without requiring them
to connect directly with Vault. This guide walks you through the configuration process.

Prerequisites:
* Ability to read or create KVv2 secrets
* Ability to create GitHub fine-grained or personal tokens (or a GitHub application) with access to modify organization and/or repository secrets
* Ability to create sync destinations and associations on your Vault server

## Setup

1. To get started with syncing Vault secrets to your GitHub, you will need a configured [GitHub application](#github-application) or an
  [access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)
  that has write permission on the target sync location in GitHub for "Secrets". The "Secrets" permissions in GitHub automatically includes read-only "Metadata" access.


<Warning title="Pitfalls of using an access token">

  Access tokens are tied to a user account and can be revoked at any time, causing disruptions to the sync process.
  GitHub applications are long-lived and do not expire. Using a GitHub application for authentication is preferred over using a personal access token.
  
</Warning>

### Repositories

Use `vault write` to configure a repository sync destination with an access token:

  ```shell-session
  $ vault write sys/sync/destinations/gh/DESTINATION_NAME \
      access_token="GITHUB_ACCESS_TOKEN"                  \
      secrets_location="GITHUB_SECRETS_LOCATION"          \
      repository_owner="GITHUB_OWNER_NAME"                \
      repository_name="GITHUB_REPO_NAME"
  ```

  For example:

  <CodeBlockConfig hideClipboard>

  ```
  $ vault write sys/sync/destinations/gh/hcrepo-sandbox       \
      access_token="github_pat_11ABC000000000000000000000DEF" \
      secrets_location="repository"                           \
      repository_owner="hashicorp"                            \
      repository_name="hcrepo"

  Key                   Value
  ---                   -----
  connection_details    map[access_token:***** secrets_location:repository repository_owner:hashicorp repository_name:hcrepo]
  name                  hcrepo-sandbox
  type                  gh
  ```

  </CodeBlockConfig>

### Environments

Use `vault write` to configure an environment sync destination:

  ```shell-session
  $ vault write sys/sync/destinations/gh/DESTINATION_NAME \
      access_token="GITHUB_ACCESS_TOKEN"                  \
      secrets_location="GITHUB_SECRETS_LOCATION"          \
      repository_owner="GITHUB_OWNER_NAME"                \
      repository_name="GITHUB_REPO_NAME"                  \
      environment_name="GITHUB_ENVIRONMENT_NAME"
  ```

  For example:

  <CodeBlockConfig hideClipboard>

  ```
  $ vault write sys/sync/destinations/gh/hcrepo-sandbox       \
      access_token="github_pat_11ABC000000000000000000000DEF" \
      secrets_location="repository"                           \
      repository_owner="hashicorp"                            \
      repository_name="hcrepo"                                \
      environment_name="sandbox"

  Key                   Value
  ---                   -----
  connection_details    map[access_token:***** secrets_location:repository environment_name:sandbox repository_owner:hashicorp repository_name:hcrepo]
  name                  hcrepo-sandbox
  type                  gh
  ```

  </CodeBlockConfig>

### Organizations

@include 'alerts/beta.mdx'


Beta limitations:

- You cannot update visibility (`organization_visibility`) after creating a
  secrets sync destination.
- You cannot update the list of repositories with access to synced secrets
  (`selected_repository_names`) after creating a secrets sync destination.

Sync secrets to GitHub organization to share those secrets across repositories 
in the organizations. You choose to make secrets global to the organization,
limited to private/internal repos, or limited to specifically named repositories.

Refer to the [Secrets sync API docs](/vault/docs/sync/github#api) for detailed
configuration information.

<Warning>

  Organization secrets are
  [not visible to private repositories for GitHub Free accounts](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-an-organization).

</Warning>

Use `vault write` to configure an organization sync destination:

  ```shell-session
  $ vault write sys/sync/destinations/gh/DESTINATION_NAME \
      access_token="GITHUB_ACCESS_TOKEN"                  \
      secrets_location="GITHUB_SECRETS_LOCATION"          \
      organization_name="ORGANIZATION_NAME"               \
      organization_visibility="ORGANIZATION_VISIBILITY"
  ```

  For example:

  <CodeBlockConfig hideClipboard>

  ```
  $ vault write sys/sync/destinations/gh/hcrepo-sandbox       \
      access_token="github_pat_11ABC000000000000000000000DEF" \
      secrets_location="organization"                         \
      organization_name="hashicorp"                           \
      organization_visibility="selected"                      \
      selected_repository_names="hcrepo-1,hcrepo-2"

  Key                   Value
  ---                   -----
  connection_details    map[access_token:***** secrets_location:organization organization_name:hashicorp organization_visibility:all selected_repository_names:[hcrepo-1 hcrepo-2]]
  name                  hcrepo-sandbox
  type                  gh
  ```

  </CodeBlockConfig>


## Usage

1. If you do not already have a KVv2 secret to sync, mount a new KVv2 secrets engine.

  ```shell-session
  $ vault secrets enable -path=my-kv kv-v2
  ```

  **Output:**

  <CodeBlockConfig hideClipboard>

  ```
  Success! Enabled the kv-v2 secrets engine at: my-kv/
  ```

  </CodeBlockConfig>

1. Create secrets you wish to sync with a target GitHub repository for Actions.

  ```shell-session
  $ vault kv put -mount='my-kv' my-secret key1='val1' key2='val2'
  ```

  **Output:**

  <CodeBlockConfig hideClipboard>

  ```plaintext
  ==== Secret Path ====
  my-kv/data/my-secret

  ======= Metadata =======
  Key                Value
  ---                -----
  created_time       <timestamp>
  custom_metadata    <nil>
  deletion_time      n/a
  destroyed          false
  version            1
  ```

  </CodeBlockConfig>

1. Create an association between the destination and a secret to synchronize.

  ```shell-session
  $ vault write sys/sync/destinations/gh/my-dest/associations/set \
      mount='my-kv' \
      secret_name='my-secret'
  ```

  **Output:**

  <CodeBlockConfig hideClipboard>

  ```plaintext
  Key                   Value
  ---                   -----
  associated_secrets    map[kv_1234/my-secret:map[accessor:kv_1234 secret_name:my-secret sync_status:SYNCED updated_at:<timestamp>>]]
  store_name            my-dest
  store_type            gh
  ```

  </CodeBlockConfig>

1. Navigate to your GitHub repository settings to confirm your secret was successfully created.

Moving forward, any modification on the Vault secret will be propagated in near-real time to its GitHub secrets
counterpart. Creating a new secret version in Vault will create a new version in GitHub. Deleting the secret
or the association in Vault will delete the secret in GitHub as well.

## Security

<Note>

Vault syncs secrets differently depending on whether you have configured 
`secret-key` or `secret-path` [granularity](/vault/docs/sync#granularity):

- `secret-key` granularity splits KVv2 secrets from Vault into key-value pairs
  and stores the pairs as distinct entries in GitHub. For example,
  `secrets.key1="val1"` and `secrets.key2="val2"`.

- `secret-path` granularity stores secrets as a single JSON string that contains
  all the associated key-value pairs. For example, `{"key1":"val1", "key2":"val2"}`.

Since GitHub limits secrets to single-value secrets, the sync granularity defaults to `secret-key`.

</Note>

If using the secret-path granularity, it is strongly advised to mask individual values for each sub-key to prevent the
unintended disclosure of secrets in any GitHub Action outputs. The following snippet illustrates how to mask each secret values:

```yaml
  name: Mask synced secret values

  on:
    workflow_dispatch

  jobs:
    synced-secret-examples:
      runs-on: ubuntu-latest
      steps:
        - name: ✓ Mask synced secret values
          run: |
            for v in $(echo '${{ secrets.VAULT_KV_1234_MY_SECRET }}' | jq -r '.[]'); do
              echo "::add-mask::$v"
            done
```

If the GitHub destination uses the default `secret-key` granularity, the values are masked by GitHub automatically.

## GitHub application

Instead of authenticating with a personal access token, you can choose to
authenticate with a
[custom GitHub application](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app).

Start by following the GitHub instructions for
[installing a GitHub app](https://docs.github.com/en/apps/using-github-apps/installing-your-own-github-app).
to install your GitHub application on a specified repository and note the
assigned installation ID.

<Tip title="Your installation ID is in the app URL">

  You can find your assigned installation ID in the URL path parameter:
  `https://github.com/settings/installations/<INSTALLATION_ID>`

</Tip>

Then add your GitHub application to your Vault instance.

To use your GitHub application with Vault:

- The application must have permission to read and write secrets.
- You must generate a private key for the application on GitHub.
- The application must be installed on the repository you want to sync secrets with.
- You must know the application ID assigned by GitHub.
- You must know the installation ID assigned by GitHub.

Callback, redirect URLs, and webhooks are not required at this time.

To configure the application in Vault, use `vault write` with the
`sys/sync/github-apps` endpoint to assign a unique name and set the relevant
information:

<CodeBlockConfig hideClipboard>

```shell-session
$ vault write sys/sync/github-apps/<APP_NAME> \
  app_id=<APP_ID> \
  private_key=@/path/to/private/key

Key            Value
---            -----
app_id         <app-id>
fingerprint    <fingerprint>
name           <app-name>
private_key    *****
```
</CodeBlockConfig>

<Tip title="Fingerprint verification">

Vault returns the fingerprint of the private_key provided to ensure that the
correct private key was configured and that it was not tampered with along the way.
You can compare the fingerprint to the one provided by GitHub.
For more information, see [Verifying private keys](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps#verifying-private-keys).

</Tip>


Next, use `vault write` with the `sys/sync/destinations/gh` endpoint to
configure a GitHub destination that references your new GitHub application:

<CodeBlockConfig hideClipboard>

```shell-session
$ vault write sys/sync/destinations/gh/<DESTINATION_NAME> \
installation_id=<INSTALLATION_ID>                         \
repository_owner=<GITHUB_USER>                            \
repository_name=<MY_REPO_NAME>                            \
app_name=<APP_NAME>

Key                   Value
---                   -----
connection_details    map[app_config:map[app_name:<app-name>] installation_id:<installation-id> repository_name:<repo-name> repository_owner:<repo-owner>]
name                  my-dest
options               map[custom_tags:map[] granularity_level:secret-key secret_name_template:VAULT_{{ .MountAccessor | uppercase }}_{{ .SecretPath | uppercase }}_{{ .SecretKey | uppercase }}]
type                  gh
```
</CodeBlockConfig>

You can now [use your GitHub application to sync secrets with your GitHub repository](#usage).

## API

Please see the [secrets sync API](/vault/api-docs/system/secrets-sync) for more details.
