---
title: "Configuration — Git for Laravel | Roundly"
description: "Every config key and default — per-provider URLs, tokens, retries and rate limits, GitHub App and OAuth, caching, logging, webhooks, batching."
url: https://roundly-consulting.com/open-source/docs/git-for-laravel/configuration
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Git for Laravel](https://roundly-consulting.com/open-source/docs/git-for-laravel.md)

# Configuration

The published config/git.php holds one block per provider plus shared blocks for caching, logging, webhooks and batching. GitHub’s block in full, with GitLab and Bitbucket abbreviated where they repeat the same shape:

```php
return [
    'providers' => [
        'github' => [
            'url' => env('GITHUB_API_URL', 'https://api.github.com'),
            'token' => env('GITHUB_TOKEN'),
            'webhook_secret' => env('GITHUB_WEBHOOK_SECRET'),
            'timeout' => env('GITHUB_TIMEOUT', 10),
            'retry' => [
                'times' => env('GITHUB_RETRY_TIMES', 1),
                'backoff' => env('GITHUB_RETRY_BACKOFF', 0),
            ],
            'rateLimits' => [
                'enabled' => env('GITHUB_RATELIMIT_ENABLED', true),
                'owner' => env('GITHUB_RATELIMIT_OWNER', 'app'),
                'maxAttempts' => env('GITHUB_RATELIMIT', 5000),
                'timespan' => env('GITHUB_RATELIMIT_TIMESPAN', 'hour'), // second | minute | hour | day
                'adaptive' => env('GITHUB_RATELIMIT_ADAPTIVE', true),
                'max_wait' => env('GITHUB_RATELIMIT_MAX_WAIT'),         // ms; null = wait/pace
                'jitter' => env('GITHUB_RATELIMIT_JITTER'),             // ms; null = none
            ],
            'options' => [
                'headers' => [
                    'User-Agent' => env('GIT_USER_AGENT', env('APP_NAME', 'GitHttp/1.0')),
                    'X-GitHub-Api-Version' => env('GITHUB_API_VERSION', '2022-11-28'),
                ],
            ],

            // GitHub App authentication (self-refreshing installation tokens).
            // When id, installation_id and private_key are all set, Git::github() mints
            // installation tokens automatically; otherwise it uses token.
            // Git::githubApp() needs only id + private_key.
            'app' => [
                'id' => env('GITHUB_APP_ID'),
                'installation_id' => env('GITHUB_APP_INSTALLATION_ID'),
                'private_key' => env('GITHUB_APP_PRIVATE_KEY'), // PEM string or file path
                'slug' => env('GITHUB_APP_SLUG'),               // builds the install URL
                'permissions' => [                               // default scope for a per-operation mint
                    'contents' => 'write',
                    'pull_requests' => 'write',
                    'metadata' => 'read',
                ],
            ],

            // OAuth credentials (self-refreshing access tokens).
            'oauth' => [
                'client_id' => env('GITHUB_OAUTH_CLIENT_ID'),
                'client_secret' => env('GITHUB_OAUTH_CLIENT_SECRET'),
                'token_url' => env('GITHUB_OAUTH_TOKEN_URL', 'https://github.com/login/oauth/access_token'),
            ],
        ],

        'gitlab' => [
            'url' => env('GITLAB_API_URL', 'https://gitlab.com'),
            'token' => env('GITLAB_TOKEN'),
            'webhook_secret' => env('GITLAB_WEBHOOK_SECRET'),
            // timeout, retry and options follow the same shape with GITLAB_* keys
            'rateLimits' => [
                'maxAttempts' => env('GITLAB_RATELIMIT', 10),
                'timespan' => env('GITLAB_RATELIMIT_TIMESPAN', 'second'),
                // enabled, owner, adaptive, max_wait, jitter as above
            ],
            'oauth' => [
                'client_id' => env('GITLAB_OAUTH_CLIENT_ID'),
                'client_secret' => env('GITLAB_OAUTH_CLIENT_SECRET'),
                'token_url' => env('GITLAB_OAUTH_TOKEN_URL', 'https://gitlab.com/oauth/token'),
            ],
        ],

        'bitbucket' => [
            'url' => env('BITBUCKET_API_URL', 'https://api.bitbucket.org'),
            'token' => env('BITBUCKET_TOKEN'),
            'webhook_secret' => env('BITBUCKET_WEBHOOK_SECRET'),
            // timeout, retry and options follow the same shape with BITBUCKET_* keys
            'rateLimits' => [
                'maxAttempts' => env('BITBUCKET_RATELIMIT', 1000),
                'timespan' => env('BITBUCKET_RATELIMIT_TIMESPAN', 'hour'),
            ],
        ],
    ],

    'cache' => [
        'enabled' => env('GIT_CACHE_ENABLED', false),
        'store' => env('GIT_CACHE_STORE'),
        'ttl' => env('GIT_CACHE_TTL', 3600),
    ],

    'logging' => [
        'enabled' => env('GIT_LOGGING_ENABLED', false),
        'channel' => env('GIT_LOGGING_CHANNEL'),
    ],

    'webhooks' => [
        // Handed over raw and parsed strictly where it is read: 1/true/on/yes enable the
        // route, 0/false/off/no keep it off, and anything else throws at boot.
        'enabled' => env('GIT_WEBHOOKS_ENABLED', false),
        'path' => env('GIT_WEBHOOKS_PATH', 'git/webhooks'),
        'middleware' => ['api'],
    ],

    'batch' => [
        'concurrency' => env('GIT_BATCH_CONCURRENCY', 25),
    ],
];
```

## Every key

| Key | Default | Purpose |
| --- | --- | --- |
| `providers.<name>.url` | Forge API URL | API base URL — api.github.com, gitlab.com, api.bitbucket.org. For GitHub Enterprise Server use https://ghe.example.com/api/v3; clone and install URLs are then built on the web host https://ghe.example.com. Unset or blank (GITHUB\_API\_URL=) uses the public API; a non-string value throws. |
| `providers.<name>.token` | null | Default access token — Git::github() and friends use it when you pass no credential. |
| `providers.<name>.webhook_secret` | null | Verifies inbound webhooks; also the default secret when registering a webhook. Unset or blank means no secret, so nothing verifies. |
| `providers.<name>.timeout` | 10 | HTTP request timeout in seconds, 0–3600 (0 = none). |
| `providers.<name>.retry.times` | 1 | Attempts (0–100) for a read that hits a dropped connection, a 429 or a 5xx; writes are never retried. retry may also be a plain int — the attempts. |
| `providers.<name>.retry.backoff` | 0 | Backoff between read retries, in ms (0–600000). |
| `providers.<name>.rateLimits.enabled` | true | Client-side throttling; false sends with no limiter at all. |
| `providers.<name>.rateLimits.owner` | app | Throttle bucket key (git:<provider>:<owner>:<digest>, a digest of the credential), so each credential keeps its own budget. Blank is not set (app); non-string throws. |
| `providers.<name>.rateLimits.maxAttempts` | 5000 / 10 / 1000 | Requests per timespan, at least 1 (GitHub / GitLab / Bitbucket). |
| `providers.<name>.rateLimits.timespan` | hour / second / hour | second, minute, hour or day; anything else throws (an unset or blank key means minute). |
| `providers.<name>.rateLimits.adaptive` | true | Honour the forge’s own Retry-After / X-RateLimit-\* headers. |
| `providers.<name>.rateLimits.max_wait` | null | Max defer in ms (0 or more) before failing fast; null or blank waits and paces instead. |
| `providers.<name>.rateLimits.jitter` | null | Random jitter in ms (0 or more) added to each defer; null or blank adds none. |
| `providers.<name>.options.headers` | User-Agent | Extra HTTP options sent with every request; GitHub also pins X-GitHub-Api-Version 2022-11-28. |
| `providers.github.app.id` | null | GitHub App id; with installation\_id and private\_key set, Git::github() mints installation tokens. Unset or blank means no app is configured; a non-string value (an integer, say) throws naming the key instead of falling back to token. |
| `providers.github.app.installation_id` | null | Installation id for the default app credential. |
| `providers.github.app.private_key` | null | App private key — a PEM string or a file path. |
| `providers.github.app.slug` | null | The app’s public slug; required by installUrl(). |
| `providers.github.app.permissions` | contents, pull\_requests: write; metadata: read | Permission set used by InstallationTokenScope::forRepositories(). |
| `providers.<github\|gitlab>.oauth.client_id` | null | OAuth client id, read by OauthToken::forProvider(). |
| `providers.<github\|gitlab>.oauth.client_secret` | null | OAuth client secret, read by OauthToken::forProvider(). |
| `providers.<github\|gitlab>.oauth.token_url` | Forge token URL | OAuth endpoint used to refresh access tokens. |
| `cache.enabled` | false | Store ETags and serve 304 Not Modified responses from cache. |
| `cache.store` | Default store | Cache store for conditional requests and for minted App/OAuth tokens. Blank is not set (default store); non-string throws. |
| `cache.ttl` | 3600 | Cached-response TTL in seconds, at least 1. |
| `logging.enabled` | false | Log method, URL, status and duration at debug level — never tokens or bodies. |
| `logging.channel` | Default channel | Log channel for request logging. Blank is not set (default channel); non-string throws. |
| `webhooks.enabled` | false | Register the POST {path}/{provider} webhook route. Unset or blank (GIT\_WEBHOOKS\_ENABLED=null, GIT\_WEBHOOKS\_ENABLED=) means off — for the route, the URL webhooks()->register() derives, and about alike. |
| `webhooks.path` | git/webhooks | Base path of the webhook route. Blank is not set and uses git/webhooks; surrounding slashes are trimmed (/hooks/ is hooks); a slash-only path or a non-string throws — never the site root. |
| `webhooks.middleware` | \['api'\] | Middleware applied to the webhook route. |
| `batch.concurrency` | 25 | Max concurrent requests per pool (1–1000); larger inputs are chunked. |

## Environment

The common knobs are env-driven, so most apps never publish the config at all:

```bash
GITHUB_TOKEN=ghp_...
GITLAB_TOKEN=glpat-...
BITBUCKET_TOKEN=...

GITHUB_WEBHOOK_SECRET=...
GIT_WEBHOOKS_ENABLED=true

GIT_CACHE_ENABLED=true
GIT_CACHE_STORE=redis
GIT_LOGGING_ENABLED=true
```

- Tokens and secrets: GITHUB\_TOKEN, GITLAB\_TOKEN, BITBUCKET\_TOKEN and the matching \*\_WEBHOOK\_SECRET.
- Per-provider transport: \*\_API\_URL, \*\_TIMEOUT, \*\_RETRY\_TIMES, \*\_RETRY\_BACKOFF and the \*\_RATELIMIT\* keys.
- GitHub App: GITHUB\_APP\_ID, GITHUB\_APP\_INSTALLATION\_ID, GITHUB\_APP\_PRIVATE\_KEY, GITHUB\_APP\_SLUG.
- OAuth: GITHUB\_OAUTH\_CLIENT\_ID, GITHUB\_OAUTH\_CLIENT\_SECRET, GITHUB\_OAUTH\_TOKEN\_URL and the GitLab equivalents.
- Shared: GIT\_CACHE\_ENABLED, GIT\_CACHE\_STORE, GIT\_CACHE\_TTL, GIT\_LOGGING\_ENABLED, GIT\_LOGGING\_CHANNEL, GIT\_WEBHOOKS\_ENABLED, GIT\_WEBHOOKS\_PATH, GIT\_BATCH\_CONCURRENCY, GIT\_USER\_AGENT, GITHUB\_API\_VERSION.

.env delivers every value as a string, and the package reads them as what they spell: an integer key takes a numeric string (GITHUB\_TIMEOUT=45 is 45 seconds) and throws package-toolkit’s InvalidConfigurationException naming the key when the value isn’t an integer in its range; a boolean key reads 1/true/on/yes as on and 0/false/off/no as off — so GIT\_WEBHOOKS\_ENABLED=off really is off — and throws the same exception for anything else (GIT\_WEBHOOKS\_ENABLED=disabled fails at boot rather than silently keeping the route off). The same goes for every other setting: a timespan outside second/minute/hour/day, a non-integer max\_wait or jitter, and a non-string string setting all throw naming the key. Only a key that is not set — absent, null or blank (a host’s KEY=) — takes its default (or stays off, for an optional one).

App and OAuth tokens are cached so they survive across requests — point cache.store at a shared store (Redis, database, file) rather than the array driver when you use them.

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/git-for-laravel.md)
