Custom VCS
Custom VCS integrations let you connect any Git or Mercurial version control system to Pulumi Deployments, including self-hosted and third-party servers. You configure webhooks on your VCS server to notify Pulumi Cloud when commits are pushed, and Pulumi automatically triggers deployments for matching stacks.
Unlike the first-party GitHub, GitLab, and Azure DevOps integrations, Custom VCS uses ESC environments for credential management and requires manual webhook configuration. It supports push-to-deploy but does not support pull request comments, commit status checks, or review stacks.
Neo, Pulumi’s AI assistant, can clone and push to Git and Mercurial repositories registered with a Custom VCS integration using the credentials from the integration’s ESC environment. Neo cannot open pull requests or create new repositories on Custom VCS servers at this time. Those operations require VCS-specific APIs only available through native integrations.
Prerequisites
- A Pulumi Cloud organization
- A Git or Mercurial VCS server accessible from Pulumi Cloud (for cloning)
- Org admin access in Pulumi Cloud
- An ESC environment containing your VCS credentials (see Configure credentials in ESC)
Configure the integration
- Sign in to your Pulumi account.
- Navigate to Management > Version control.
- Select Add integration and choose Custom VCS.
- Enter a Name for the integration (e.g., “My Git Server”). Names must be unique within your organization.
- Enter a Base URL — the URL prefix for your repositories (e.g.,
https://git.example.com/myorg). Repository names are appended to this URL to form clone URLs. - Select the ESC environment containing your VCS credentials, in
project/envNameformat (e.g.,vcs-creds/git-prod). - Select Save.
By default, Custom VCS integrations are created with a VCS type of git. To configure a Mercurial integration, create the integration via the Pulumi Cloud REST API with vcsType set to hg.
After saving, Pulumi displays a webhook URL and webhook secret.
Configure credentials in ESC
Custom VCS integrations use an ESC environment to store VCS credentials. The same credential structure works for both Git and Mercurial integrations. Create an environment with one of the following authentication methods:
Personal access token
values:
personalAccessToken:
fn::secret: "your-token-here"
SSH key
values:
sshPrivateKey:
fn::secret: |
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
sshPassword:
fn::secret: "optional-passphrase"
Basic auth
values:
username: "deploy-bot"
password:
fn::secret: "your-password"
Credentials are resolved at deployment time using the access permissions of the user who configured the integration. If that user loses access to the ESC environment, deployments will fail with an authentication error.
Add repositories
Custom VCS integrations do not auto-discover repositories from your VCS server. You must add each repository manually.
- Navigate to Management > Version control and select your Custom VCS integration.
- Select Add repository.
- Enter the repository name or path (e.g.,
infra-prodorteam/infra-prod). - Optionally enter a display name for the repository.
The repository name is joined with the integration’s base URL to form the clone URL. For example, a base URL of https://git.example.com/myorg and a repository name of infra-prod produces the clone URL https://git.example.com/myorg/infra-prod.
To remove a repository, select the repository from the integration detail page and choose Remove.
Configure webhooks
After creating the integration and adding repositories, configure your VCS server to send webhook notifications to Pulumi Cloud.
Webhook URL
Configure your VCS server to send POST requests to:
https://api.pulumi.com/workflow/generic-vcs
Required headers
Your webhook requests must include the following headers:
| Header | Description | Example |
|---|---|---|
X-Generic-VCS-Integration-ID | Your integration UUID (shown on the integration detail page) | a1b2c3d4-e5f6-7890-abcd-ef1234567890 |
X-Generic-VCS-Signature | HMAC-SHA256 signature of the request body | v1=abc123def456... |
Content-Type | Must be application/json | application/json |
Payload format
The request body must be a JSON object with the following fields:
{
"event": "push",
"commit": "abc123def456789...",
"branch": "main",
"repoUrl": "https://git.example.com/myorg/infra-prod",
"sender": "deploy-bot",
"changedFiles": ["infra/main.go", "Pulumi.yaml"]
}
| Field | Type | Required | Description |
|---|---|---|---|
event | string | Yes | Event type. Only push events trigger deployments. |
commit | string | Yes | Full commit SHA (Git) or changeset ID (Mercurial) |
branch | string | Yes | Branch name that was pushed to |
repoUrl | string | Yes | Full repository URL (must match a configured repository) |
sender | string | No | Identifier for the user who pushed |
changedFiles | string[] | No | File paths changed in this push. Enables path filtering. If omitted, path filters are skipped and all pushes trigger deployments. |
HMAC signing
Pulumi verifies webhook authenticity using HMAC-SHA256 signatures. To sign a request:
- Compute
HMAC-SHA256(webhook_secret, raw_request_body)using the webhook secret provided during integration setup. - Hex-encode the result.
- Set the header:
X-Generic-VCS-Signature: v1=<hex-encoded-hmac>.
The v1 prefix identifies the signing algorithm version.
Deployment settings
Configure deployment behavior for Custom VCS-backed stacks under Stack > Settings > Deploy.
| Setting | Description |
|---|---|
| Push to deploy | Automatically deploy when commits are pushed to the configured branch |
| Path filters | Only trigger deployments when changed files match specified glob patterns (e.g., infrastructure/**). Requires the webhook payload to include the changedFiles field. |
Selecting a repository and branch
- Choose the Custom VCS integration (if multiple are configured).
- Select a repository from the dropdown. This list shows the repositories you have manually configured on the integration.
- Enter the target branch name.
Comparison with native integrations
Custom VCS provides webhook-driven push-to-deploy but does not include the deeper VCS platform features available with native integrations.
| Capability | Native integrations | Custom VCS |
|---|---|---|
| Push-to-deploy | Yes | Yes (via webhook) |
| PR/MR previews | Yes | No |
| Commit status checks | Yes | No |
| PR comments | Yes | No |
| Review stacks | Yes | No |
| Auto-discover repos | Yes | No (manual) |
| Branch listing | Yes | No (manual entry) |
| Path filtering | Yes | Yes (requires changedFiles in webhook) |
| Credential management | OAuth/app tokens | ESC environment |
| Mercurial support | No | Yes |
| Neo clone/push to repos | Yes | Yes (Git and Mercurial) |
| Neo pull request creation | Yes | No |
| Neo repository creation | Yes | No |
Troubleshooting
| Issue | Resolution |
|---|---|
| Webhook returns 401 Unauthorized | Verify your HMAC signature computation. Ensure you are signing the raw request body with the correct webhook secret and using HMAC-SHA256. Check that the v1= prefix is included. |
| Deployments not triggering | Check that repoUrl in your webhook payload matches a configured repository URL. Verify the branch matches your deployment settings. Confirm event is set to push and deploy on push is enabled for the stack. |
| Clone fails with authentication error | Verify credentials in your ESC environment. Check that the configuring user still has access to the ESC environment. |
| Integration shows broken credentials | The ESC environment may have been deleted or the configuring user may have lost access. Update the ESC environment reference or have an admin reconfigure the integration. |
| Path filters not working | Path filtering requires the changedFiles field in your webhook payload. If omitted, all pushes trigger deployments regardless of path filter settings. |
Thank you for your feedback!
If you have a question about how to use Pulumi, reach out in Community Slack.
Open an issue on GitHub to report a problem or suggest an improvement.