paeire/laravel-secrets-manager

Load AWS Secrets Manager secrets into Laravel's configuration at boot, with optional RDS rotation support and configurable caching.

Maintainers

Package info

github.com/paeire/laravel-secrets-manager

pkg:composer/paeire/laravel-secrets-manager

Transparency log

Statistics

Installs: 904

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.2.1 2026-07-14 15:44 UTC

This package is auto-updated.

Last update: 2026-08-14 15:59:24 UTC


README

Load secrets from AWS Secrets Manager into Laravel's configuration at boot before the database (or anything else) is used. It supports two modes (a general "full config" secret and an RDS rotation secret), configurable caching, and a command to refresh the cache.

Requirements

  • PHP 8.2+
  • Laravel 10, 11 or 12
  • AWS credentials available through the standard AWS credential provider chain (IAM role, AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY, etc.)
  • The IAM principal needs secretsmanager:GetSecretValue on the secret(s) you load.

Installation

composer require paeire/laravel-secrets-manager

The service provider is auto-discovered. Optionally publish the config:

php artisan vendor:publish --tag=secrets-config

How it works

Secrets are loaded in the provider's register() which runs after Laravel loads its configuration but before any service (database, cache, mail, ...) is resolved. Values are written with config()->set(), so they override whatever config/*.php produced from .env, regardless of when the connection is first used.

Configuration

All settings are driven by environment variables (see config/secrets.php):

Variable Default Description
FULL_CONFIG false Enable the general secret loader.
AWS_SECRET_ID laravel-prod-secrets Secret id for the general loader.
AWS_DEFAULT_REGION us-east-1 AWS region.
RDS_ROTATION false Enable the RDS rotation loader.
AWS_SECRET_ID_RDS laravel-prod-secrets Secret id for the RDS loader.
RDS_READ_WRITE false Mirror the resolved host onto the read/write connection.
SECRETS_THROW_ON_FAILURE false Re-throw on load failure instead of only logging.
SECRETS_CACHE false Cache the general secret.
SECRETS_CACHE_STORE (default store) Cache store for the general secret.
SECRETS_CACHE_TTL 300 General secret cache TTL (seconds).
SECRETS_RDS_CACHE false Cache the RDS secret (off by default).
SECRETS_RDS_CACHE_STORE (default store) Cache store for the RDS secret.
SECRETS_RDS_CACHE_TTL 60 RDS secret cache TTL (seconds).

Mode 1 Full config

Set FULL_CONFIG=true. Each key in the secret's JSON is applied to the config repository and exported to the process environment, so it works whether your code reads it via config() or via env():

  • Keys that are Laravel config paths (e.g. database.connections.mysql.host) override that config value directly.
  • Keys that are flat variable names (e.g. RECAPTCHA_SECRET_KEY) are reachable through env('RECAPTCHA_SECRET_KEY') as well as config('RECAPTCHA_SECRET_KEY').
{
    "database.connections.mysql.host": "10.0.0.5",
    "database.connections.mysql.password": "s3cr3t",
    "RECAPTCHA_SECRET_KEY": "6Lfz...",
    "services.stripe.key": "sk_live_..."
}

Only scalar values are exported to env(); structured (array) values are applied to config() only, since environment variables can't hold them.

Mode 2 RDS rotation

Set RDS_ROTATION=true. The secret uses the standard AWS RDS rotation shape; only the keys below are mapped onto database.connections.mysql, and any extra keys (engine, dbname, dbClusterIdentifier, ...) are ignored:

{
    "username": "admin",
    "password": "rotated-password",
    "host": "my-cluster.rds.amazonaws.com",
    "port": 3306
}

With RDS_READ_WRITE=true, the resolved host is also written to database.connections.mysql.read.host and ...write.host.

Caching

General secrets change rarely, so caching avoids an AWS call on every boot. RDS rotation secrets are not cached by default on purpose: a cached, rotated password would break authentication.

Caveat: if your cache store itself depends on a secret this package loads (for example a Redis password loaded via FULL_CONFIG), point SECRETS_CACHE_STORE at a self-contained store such as file, or leave caching disabled otherwise the cache can't be read before the secret it needs is loaded.

Refreshing the cache

After rotating or changing a cached secret, clear the cache so it is re-fetched on the next boot:

php artisan secrets:refresh            # clear both caches
php artisan secrets:refresh --general  # only the general secret
php artisan secrets:refresh --rds      # only the RDS secret

The command runs in its own process, so it invalidates the cache; the fresh values are loaded on the next request/boot.

Failure handling

By default, a failure to load secrets is logged (without leaking values) and the app continues to boot. Set SECRETS_THROW_ON_FAILURE=true to fail fast when secrets are mandatory.

Testing

composer install
composer test

License

MIT