qubeek/nova-storage-info-card
A Laravel Nova card that shows storage information for your application, including disk usage and available space, so you can monitor storage health at a glance from the Nova dashboard. Easy to install and configure for local or remote disks. Keep an eye on capacity before it becomes a problem.
The card is a Laravel Nova dashboard widget that reports usage statistics for S3-style disks configured in config/filesystems.php. It is not a storage driver — it is a read-only observability card, so the first decision is whether Nova is already installed and whether you have at least one s3 disk wired up.
Install the package alongside Nova (Nova ≥ 4 is the safe assumption; verify the composer.json constraint before committing):
composer require qubeek/nova-storage-info-card
Confirm a disk exists. In config/filesystems.php you should already have something like:
'disks' => [
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
],
],
The card reads this disk through Laravel's Storage facade / Flysystem S3 adapter, so if the disk works for Storage::disk('s3')->put(...), the card has what it needs.
Publish the config (the exact tag varies — check the README, but it will be a standard vendor:publish):
php artisan vendor:publish --provider="Qubeek\NovaStorageInfoCard\CardServiceProvider"
This should drop a file such as config/nova-storage-info-card.php where you list the disk names to monitor.
Register the card in app/Providers/NovaServiceProvider.php:
public function cards()
{
return [
// ...
new \Qubeek\NovaStorageInfoCard\StorageInfoCard(),
// or, if the constructor takes a disk name:
// (new \Qubeek\NovaStorageInfoCard\StorageInfoCard('s3'))->width('1/2'),
];
}
Verify the constructor signature in the package source before assuming it takes a disk argument — with zero dependents and a zero score, don't rely on examples you find in blog posts.
First use case: add one card bound to your primary media bucket and let it render object count, total size, and (if supported) the last-modified/recency summary on the Nova dashboard. This gives you an at-a-glance sanity check that backups and uploads are landing in the right bucket without opening the AWS console.
If you cannot get the card to render at all within ~15 minutes, stop and evaluate whether a small custom Nova metric card is a better use of time — the core logic here is a couple of listObjectsV2 calls and a view.
One card per disk. The most robust pattern with S3 monitoring is to register a separate card instance per disk rather than one card with a dropdown. It keeps the dashboard scannable and lets you size/layout them independently:
public function cards()
{
return [
(new StorageInfoCard('media'))->width('1/2'),
(new StorageInfoCard('backups'))->width('1/2'),
(new StorageInfoCard('exports'))->width('1/3'),
];
}
Config-driven disks. If the package supports a disks array in the published config, prefer that over hardcoding disk names in NovaServiceProvider. It means a non-developer can add a bucket to the dashboard by editing config, and it keeps NovaServiceProvider from becoming a list of magic strings. Cache-clear after editing (php artisan config:clear) — see gotchas.
Pair with a policy/permission gate. Wrap the card registration so only admins see storage figures:
public function cards()
{
return Gate::allows('viewStorageMetrics')
? [new StorageInfoCard('s3')]
: [];
}
Storage costs and bucket names are mildly sensitive; there is no reason every Nova user needs them.
Use it as a smoke test in staging. Because the card exercises the same credentials and bucket config as your application, a broken card is often the fastest signal that an IAM policy change or a region misconfiguration has broken uploads in an environment. Put it on the staging dashboard deliberately.
Combine with application-level metrics. The card reports what is in the bucket. Pair it with your own Nova metric (queued jobs, failed uploads, media records in the DB) to detect divergence — e.g. object count flat while DB media records grow indicates orphaned uploads or a failed sync job.
Layout for the dashboard. Cards are Nova components; use ->width(), ->onlyOnDashboard(), and ->canSee() as you would with any card. Grouping storage cards into a dedicated "Storage" dashboard (Nova::mainMenu / custom dashboard class) scales better than piling them onto the main dashboard once you have more than two buckets.
Refresh strategy. For buckets with more than a few thousand objects, do not let the card run a fresh listing on every dashboard load. Either configure the package's cache TTL (if it exposes one) or schedule a small artisan command that warms a cache key the card reads. A dashboard that takes 8 seconds to load because it is paginating a 2M-object bucket will be disabled by your team within a week.
s3:ListBucket on the bucket ARN, not just s3:GetObject on bucket/*. If you only granted object-level permissions for uploads, the card will error or render zeros while uploads work fine. Also check s3:GetBucketLocation if the package resolves the region.listObjectsV2 is cheap per call but not free, and Nova dashboards get refreshed more often than you think. Budget for it, and cache aggressively on large buckets.nova constraint in the package's composer.json against your installed version.config:cache will mask config edits. In production, after changing the disk list run php artisan config:clear && php artisan config:cache. This bites people constantly.endpoint, use_path_style_endpoint => true (MinIO), and a correct region on the disk itself. The card inherits whatever the disk is configured with — do not try to fix endpoint issues from the card's config..env on the server.packages/ directory or a private VCS repo and pin the fork; that way a future Nova upgrade does not leave you waiting on a maintainer who may not exist. The code surface is small enough to own.Storage::disk('s3')->allFiles() or Storage::disk('s3')->files() — then compare. This tells you instantly whether the problem is IAM/disk config (a Laravel issue) or the card's aggregation logic (a package issue).How can I help you explore Laravel packages today?