NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages

The package works with zero host configuration — every key has a default, and the common ones are env-backed. The published config/media.php in full:

return [
    'disk' => env('MEDIA_DISK', 'public'),
    'variants_disk' => env('MEDIA_VARIANTS_DISK'),

    'media_model' => RoundlyConsulting\MediaLibrary\Models\Media::class,
    'table_name' => 'media',

    'queue_variants_by_default' => false,
    'queue_connection' => env('MEDIA_QUEUE_CONNECTION'),
    'queue_name' => env('MEDIA_QUEUE'),

    'image_driver' => env('MEDIA_IMAGE_DRIVER', 'imagick'),
    'variant' => [
        'quality' => 75,
        'background' => '#ffffff',
    ],

    'url_fallback_to_original' => false,
    'temporary_url_default_lifetime' => 5,

    'stream' => [
        'enabled' => env('MEDIA_STREAM_ENABLED', true),
        'route_prefix' => 'media',
        'middleware' => ['web'],
    ],

    'path_generator' => RoundlyConsulting\MediaLibrary\Support\DefaultPathGenerator::class,
    'file_namer' => RoundlyConsulting\MediaLibrary\Support\DefaultFileNamer::class,

    'default_visibility' => 'public',
    'max_file_size' => 1024 * 1024 * 256,

    'remote' => [
        'headers' => [],
        'timeout' => 30,
    ],

    'deduplicate' => true,
    'checksum_algorithm' => 'sha256',          // sha256|sha384|sha512|sha512/256|sha3-256|sha3-384|sha3-512
    'verify_checksum_on_read' => false,

    'placeholders' => [
        'thumbhash' => true,
        'blurhash' => true,
    ],

    'responsive' => [
        'widths' => [320, 640, 960, 1280, 1920],
    ],

    'drafts' => [
        'ttl' => 1440,
    ],

    'url_generator' => RoundlyConsulting\MediaLibrary\Support\DefaultUrlGenerator::class,
    'cdn' => [
        'enabled' => false,
        'base_url' => env('MEDIA_CDN_URL'),
        'cache_bust' => true,
        'disks' => [],
    ],
];

Every key

KeyDefaultPurpose
diskpublicDefault disk for originals (MEDIA_DISK). A blank value is not set, so public applies; a non-string value throws.
variants_disknullDefault disk for variants (MEDIA_VARIANTS_DISK); null or blank (not set) = the original's disk. A non-string value throws.
media_modelMedia::classEloquent model that persists media. Must be the packaged Media model or a subclass of it; anything else throws InvalidConfigurationException.
table_namemediaDatabase table the media model uses. A blank value is not set, so media applies; a non-string value throws.
queue_variants_by_defaultfalseQueue all variant generation by default.
queue_connectionnullQueue connection for GenerateVariantsJob (MEDIA_QUEUE_CONNECTION); null = the default connection.
queue_namenullQueue name for GenerateVariantsJob (MEDIA_QUEUE); null = the default queue.
image_driverimagickimagick or gd (MEDIA_IMAGE_DRIVER); anything else throws. With imagick configured but the extension missing, gd is used.
variant.quality75Default JPEG/WebP quality (1–100).
variant.background#ffffffFlatten colour when a transparent image is converted to JPEG.
url_fallback_to_originalfalseWhen getUrl() is asked for an un-generated variant: throw (false) or return the original’s URL (true).
temporary_url_default_lifetime5Default minutes (at least 1) for signed/temporary URLs when no expiry is passed.
stream.enabledtrueRegister the signed streaming route (MEDIA_STREAM_ENABLED, read as a boolean; anything else throws).
stream.route_prefixmediaURI prefix for the streaming route.
stream.middleware['web']Middleware stack for the streaming route; Laravel’s signed is always appended.
path_generatorDefaultPathGenerator::classDirectory layout for a media’s files. A class that is not a PathGenerator throws when resolved.
file_namerDefaultFileNamer::classOriginal and variant file naming. A class that is not a FileNamer throws when resolved.
default_visibilitypublicpublic or private for new media when a bucket or add doesn’t set it. Anything else throws — a typo is never stored (it would read as public).
max_file_size256 MBPackage-level size cap, enforced on every add (and remote download) and emitted by the derived rules; a bucket’s maxFileSize() overrides it. null or blank (not set) = no limit, otherwise at least 1 byte.
remote.headers[]Extra HTTP headers (name ⇒ value) for addFromUrl() / addMediaFromUrl().
remote.timeout30HTTP timeout in seconds (at least 1) for addFromUrl() / addMediaFromUrl().
deduplicatetrueReuse storage for identical bytes.
checksum_algorithmsha256Content checksum (dedup key + integrity): sha256, sha384, sha512, sha512/256, sha3-256, sha3-384 or sha3-512 — anything else throws InvalidConfigurationException.
verify_checksum_on_readfalseRe-hash on read; throws on drift.
placeholders.thumbhashtrueCompute a ThumbHash LQIP on add.
placeholders.blurhashtrueCompute a Blurhash LQIP on add.
responsive.widths[320…1920]Default responsive srcset ladder (positive integers), overridable per bucket.
drafts.ttl1440Minutes (at least 1) before an unbound draft is prunable.
url_generatorDefaultUrlGenerator::classURL building strategy. Takes precedence over the CDN generator; a class that is not a UrlGenerator throws when resolved.
cdn.enabledfalseRewrite public URLs onto a CDN host.
cdn.base_urlnullCDN base URL (MEDIA_CDN_URL), e.g. https://cdn.example.com.
cdn.cache_busttrueAppend ?v={updated_at} to public URLs.
cdn.disks[]Limit CDN rewriting to these disks; [] = all public disks.

Every bool switch is parsed as a boolean wherever it is read: true/1/on/yes turn it on and false/0/off/no turn it off, so a switch you feed from .env in your published config behaves as written. A blank value (MEDIA_STREAM_ENABLED=) is not set, so the default applies. Anything else (say MEDIA_STREAM_ENABLED=disabled) throws InvalidConfigurationException instead of quietly reading as the default.

Every other setting is read just as strictly. A setting that is not set — absent, null, or blank like a host’s MEDIA_VARIANTS_DISK= — takes its default, and an optional one (variants_disk, the queue connection and name, max_file_size, cdn.base_url) stays unset; a blank path_generator, file_namer or url_generator binds the packaged class. Integers accept an int or a plain integer string (every env value is a string), so thirty or 5.5 throws rather than becoming 0 — which for remote.timeout would have meant no timeout at all. A non-string disk, queue, table name, route prefix, background or CDN base URL throws, and so does a default_visibility, image_driver or checksum_algorithm typo, or a junk entry in a width, middleware, header or CDN-disk list. php artisan about renders a broken setting as INVALID.

Environment

The common knobs are env-driven, so you rarely publish the config at all:

MEDIA_DISK=public
MEDIA_VARIANTS_DISK=s3
MEDIA_IMAGE_DRIVER=imagick
MEDIA_STREAM_ENABLED=true
MEDIA_CDN_URL=https://cdn.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

By donating, you agree to our donation terms.

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.