---
title: "Configuration — Certificates for Laravel | Roundly"
description: "Every config/certificates.php key with its env var and default — driver, registry, renewal, lock, status cache and alerts."
url: https://roundly-consulting.com/open-source/docs/certificates-for-laravel/configuration
language: en
---

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

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

# Configuration

The published config/certificates.php in full, comments removed:

```php
return [
    'default' => env('CERTIFICATES_DRIVER', 'kubernetes'),
    'connection' => env('CERTIFICATES_DB_CONNECTION'),

    'model' => RoundlyConsulting\Certificates\Models\Certificate::class,
    'table' => 'certificates',
    'key_type' => env('CERTIFICATES_KEY_TYPE', 'bigint'),

    'renewal' => [
        'threshold_days' => env('CERTIFICATES_RENEW_THRESHOLD_DAYS', 21),
        'queue' => env('CERTIFICATES_RENEW_QUEUE'),
    ],

    'name_prefix' => env('CERTIFICATES_NAME_PREFIX', 'generated-tls-'),

    'lock' => [
        'name' => env('CERTIFICATES_LOCK_NAME', 'certificates:generate'),
        'locked_for_seconds' => env('CERTIFICATES_LOCK_SECONDS', 600),
    ],

    'status_cache' => [
        'enabled' => env('CERTIFICATES_STATUS_CACHE', true),
        'store' => env('CERTIFICATES_STATUS_CACHE_STORE'),
        'ttl' => env('CERTIFICATES_STATUS_CACHE_TTL', 300),
    ],

    'alerts' => [
        'enabled' => env('CERTIFICATES_ALERTS', false),
        'notifiable' => env('CERTIFICATES_ALERTS_NOTIFIABLE'),
        'thresholds' => [
            'warning_days' => env('CERTIFICATES_ALERTS_WARNING_DAYS', 30),
            'critical_days' => env('CERTIFICATES_ALERTS_CRITICAL_DAYS', 7),
        ],
        'channels' => ['mail'],
        'register_check' => env('CERTIFICATES_ALERTS_REGISTER_CHECK', false),
    ],

    'drivers' => [
        'kubernetes' => [
            'base_url' => env('CERTIFICATES_K8S_BASE_URL', 'https://kubernetes.default.svc'),
            'token' => env('CERTIFICATES_K8S_TOKEN'),
            'token_path' => env('CERTIFICATES_K8S_TOKEN_PATH', '/var/run/secrets/kubernetes.io/serviceaccount/token'),
            'ca_path' => env('CERTIFICATES_K8S_CA_PATH', '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt'),
            'namespace' => env('CERTIFICATES_K8S_NAMESPACE', 'default'),
            'issuer' => env('CERTIFICATES_K8S_ISSUER', 'letsencrypt'),
            'issuer_kind' => env('CERTIFICATES_K8S_ISSUER_KIND', 'ClusterIssuer'),
            'ingress' => [
                'name' => env('CERTIFICATES_K8S_INGRESS_NAME'),
                'class' => env('CERTIFICATES_K8S_INGRESS_CLASS', 'nginx'),
            ],
            'service' => [
                'name' => env('CERTIFICATES_K8S_SERVICE_NAME'),
                'port' => env('CERTIFICATES_K8S_SERVICE_PORT', 80),
            ],
        ],

        'acme' => [
            'directory' => env('CERTIFICATES_ACME_DIRECTORY', 'https://acme-v02.api.letsencrypt.org/directory'),
            // Staging: https://acme-staging-v02.api.letsencrypt.org/directory
            'contact' => env('CERTIFICATES_ACME_CONTACT'),
            'account' => [
                'key_type' => env('CERTIFICATES_ACME_KEY_TYPE', 'EC'),
                'disk' => env('CERTIFICATES_ACME_ACCOUNT_DISK', 'local'),
                'key_path' => env('CERTIFICATES_ACME_ACCOUNT_KEY', 'acme/account.pem'),
                'auto_register' => env('CERTIFICATES_ACME_AUTO_REGISTER', true),
            ],
            'solver' => env('CERTIFICATES_ACME_SOLVER'),
            'http' => [
                'disk' => env('CERTIFICATES_ACME_HTTP_DISK', 'local'),
                'path' => env('CERTIFICATES_ACME_HTTP_PATH', 'acme-challenge'),
            ],
            'store' => [
                'disk' => env('CERTIFICATES_ACME_STORE_DISK', 'local'),
                'path' => env('CERTIFICATES_ACME_STORE_PATH', 'certificates'),
            ],
            'poll' => [
                'attempts' => env('CERTIFICATES_ACME_POLL_ATTEMPTS', 30),
                'seconds' => env('CERTIFICATES_ACME_POLL_SECONDS', 2),
            ],
            'verify' => env('CERTIFICATES_ACME_VERIFY', true),
        ],

        'filesystem' => [
            'disk' => env('CERTIFICATES_FS_DISK', 'local'),
            'path' => env('CERTIFICATES_FS_PATH', 'certificates'),
            'self_signed' => env('CERTIFICATES_FS_SELF_SIGNED', false),
            'self_signed_days' => env('CERTIFICATES_FS_SELF_SIGNED_DAYS', 90),
        ],

        'null' => [],
        'array' => [],
    ],
];
```

## General keys

| Key | Env | Default | Purpose |
| --- | --- | --- | --- |
| `default` | `CERTIFICATES_DRIVER` | kubernetes | Driver used when none is named — a drivers key or one registered with extend(). |
| `connection` | `CERTIFICATES_DB_CONNECTION` | null | Registry DB connection; null uses the model’s default. Override per call with on(). |
| `model` | — | Models\\Certificate::class | Registry model; a subclass swaps it everywhere, any other class throws. |
| `table` | — | certificates | Registry table name. |
| `key_type` | `CERTIFICATES_KEY_TYPE` | bigint | Key type of the certifiable owner morph: bigint, uuid or ulid (case-insensitive; anything else throws). |
| `renewal.threshold_days` | `CERTIFICATES_RENEW_THRESHOLD_DAYS` | 21 | Days before expiry a certificate counts as expiring. |
| `renewal.queue` | `CERTIFICATES_RENEW_QUEUE` | null | Queue RenewCertificateJob is dispatched onto. |
| `name_prefix` | `CERTIFICATES_NAME_PREFIX` | generated-tls- | Prefix of the derived, lowercase DNS-1123 certificate name. '' is a valid prefix — names are then the bare host. |
| `lock.name` | `CERTIFICATES_LOCK_NAME` | certificates:generate | Prefix of the per-certificate provisioning lock ({lock.name}:{certificate name}). |
| `lock.locked_for_seconds` | `CERTIFICATES_LOCK_SECONDS` | 600 | The lock’s safety expiry in seconds — keep it above your slowest issuance. Also RenewCertificateJob’s timeout and how long a row may sit in renewing before it counts as a stuck renewal. |
| `status_cache.enabled` | `CERTIFICATES_STATUS_CACHE` | true | Cache live provider status reports. |
| `status_cache.store` | `CERTIFICATES_STATUS_CACHE_STORE` | null | Cache store; null uses the default store. |
| `status_cache.ttl` | `CERTIFICATES_STATUS_CACHE_TTL` | 300 | Status cache lifetime in seconds. |
| `alerts.enabled` | `CERTIFICATES_ALERTS` | false | Raise alerts from certificates:check and on lifecycle failures. |
| `alerts.notifiable` | `CERTIFICATES_ALERTS_NOTIFIABLE` | null | Notifiable FQCN resolved from the container — it must resolve to a stored Eloquent model, so bind the class to its record (an unbound class builds an unsaved model, which throws); beats the certificate’s owner. |
| `alerts.thresholds.warning_days` | `CERTIFICATES_ALERTS_WARNING_DAYS` | 30 | Warning band of the expiry check. |
| `alerts.thresholds.critical_days` | `CERTIFICATES_ALERTS_CRITICAL_DAYS` | 7 | Critical band — the check fails inside it. |
| `alerts.channels` | — | \['mail'\] | Channels of the registry-wide check. |
| `alerts.register_check` | `CERTIFICATES_ALERTS_REGISTER_CHECK` | false | Register one registry-wide expiry check at boot. |

Driver keys are covered on the ACME & Let’s Encrypt, Kubernetes & cert-manager and Filesystem driver pages. The null and array drivers take no options.

## Environment

The common knobs are env-driven, so you rarely need to publish the file:

```bash
CERTIFICATES_DRIVER=acme
CERTIFICATES_RENEW_THRESHOLD_DAYS=21
CERTIFICATES_RENEW_QUEUE=certificates
CERTIFICATES_STATUS_CACHE_TTL=300
CERTIFICATES_ALERTS=true
CERTIFICATES_ALERTS_WARNING_DAYS=30
CERTIFICATES_ALERTS_CRITICAL_DAYS=7
```

Every bool switch accepts the usual env spellings — true/false, 1/0, on/off, yes/no — so CERTIFICATES\_ALERTS=1 turns alerts on and CERTIFICATES\_STATUS\_CACHE=off turns the cache off. Not set — absent, null or blank ('', what KEY= in .env gives) — reads as the default; anything else (a typo such as disabled) throws InvalidConfigurationException naming the key instead of quietly reading as the default.

## Strict config reads

Every other value is read just as strictly. Not set (absent, null or blank) reads as the default; a present value of the wrong shape throws InvalidConfigurationException naming the key:

- Integers take an int or a canonical integer string (env values arrive as strings), so CERTIFICATES\_RENEW\_THRESHOLD\_DAYS=five, '1.5' or a value out of range throws — never a silent 0. Bounds: renewal.threshold\_days, lock.locked\_for\_seconds, status\_cache.ttl, drivers.acme.poll.\* and drivers.filesystem.self\_signed\_days ≥ 1; alerts.thresholds.\* ≥ 0; drivers.kubernetes.service.port 1–65535.
- String settings (default, table, lock.name, disks, paths, namespace, issuer, …) must be strings, and a blank one is not set and takes its default. The optional ones (connection, queues, stores, tokens, contact, solver, notifiable) must be strings when set, and a blank value is not set, so it stays unset. name\_prefix may be '' — it is not read as unset, so names are the bare host.
- drivers.acme.account.key\_type is EC or RSA; alerts.channels is a list of channel names; alerts.notifiable must resolve to a stored Eloquent model (bind the class in the container: $this->app->bind(OpsTeam::class, fn () => OpsTeam::query()->firstOrFail())) and drivers.acme.solver to an AcmeChallengeSolver.
- key\_type is bigint, uuid or ulid, case-insensitive — unset or blank reads as bigint, anything else throws. The migration reads it, so set it before you migrate.

## Locking and TLS verification

lock.\* configures a cache lock taken per certificate ({lock.name}:{certificate name}) while it is provisioned — other certificates are never blocked. While it is held, issue() throws ProvisioningInProgressException (the registry row is left untouched) and generate() returns false. renew() takes the same lock, so a renewal never races an issuance or another renewal of the same certificate. Keep lock.locked\_for\_seconds above your slowest issuance: an ACME order polls each authorization and the order for up to poll.attempts × poll.seconds. The default is ten minutes (600). It also sets RenewCertificateJob’s timeout and how long a row may sit in renewing before it counts as a stuck renewal and is renewed again.

drivers.kubernetes.ca\_path and drivers.acme.verify read the same way: a CA bundle path, null or an empty value (or true, 1, on, yes) for the system CA bundle, and only a false value (false, 0, off, no) disables TLS verification — not recommended. Any other string is a bundle path (a typo fails the TLS handshake — it never disables verification), and a value that is neither a string nor a boolean (an array, a float) throws InvalidConfigurationException.

## Derived certificate names

Every certificate gets a deterministic name: name\_prefix plus the primary domain, lowercased and folded into a DNS-1123 subdomain — only a–z, 0–9 and hyphens, a leading \*. becomes wildcard-, and an over-long name is cut to 253 characters with a hash suffix. Domains are case-insensitive, so App.Example.com and app.example.com share one name and one registry row. The name is the Kubernetes secret name, the directory name on a store disk and half of the registry’s unique (driver, name) key:

```php
Certificates::certificateName('app.example.com'); // "generated-tls-app-example-com"
Certificates::certificateName('App.Example.com'); // "generated-tls-app-example-com" — the same certificate
Certificates::certificateName('*.Example.com');   // "generated-tls-wildcard-example-com"
```

## 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/certificates-for-laravel.md)
