A premium, feature-rich PHP SDK and Laravel integration for the Hetzner Storage Box API, featuring rate-limit handling, automatic retries, and concurrent batch operations.
- 100% Endpoint Coverage: Complete implementation of all storage boxes, subaccounts, snapshots, actions, and locations.
- Fail-Safe Retries & Backoff: Robust exponential backoff and rate-limit parsing handling
RateLimit-Resetresponse headers automatically. - Concurrently Pooled Processing: Execute calls asynchronously or concurrently in batches.
- Dynamic Filter Builder: Fluent query-building for filtering, page indexing, and sorting.
- Type-Safe DTOs: Automated data hydration into standard PHP DTO structures.
- Custom Exceptions: Specialized mapping of API status codes.
Install the package via Composer:
composer require ghostcompiler/laravel-hetzner-storageboxPublish the configuration file using our simplified command installer:
php artisan ghost:storagebox installAdd your Hetzner Storage Box Console API Token to your .env file:
HETZNER_STORAGEBOX_TOKEN=your_api_token_here
HETZNER_STORAGEBOX_TIMEOUT=30
HETZNER_STORAGEBOX_RETRIES=3
HETZNER_STORAGEBOX_RETRY_BACKOFF=100
HETZNER_STORAGEBOX_LOGGING_ENABLED=trueYou can access the SDK in three ways:
- Facade (Recommended for Laravel environments)
use GhostCompiler\Hetzner\StorageBox\Facades\HetznerStorageBox;
$boxes = HetznerStorageBox::storageBoxes()->get();- Global Helper (Convenience function)
$boxes = HetznerStorageBox()->storageBoxes()->get();- Dynamic Token Authentication (For multi-tenant or multi-account systems)
$boxes = HetznerStorageBox('your_dynamic_token_here')->storageBoxes()->get();use GhostCompiler\Hetzner\StorageBox\Facades\HetznerStorageBox;
// List the first 50 storage boxes matching a name
$boxes = HetznerStorageBox::storageBoxes()
->filter(['name' => 'box-01'])
->perPage(50)
->page(1)
->get();
foreach ($boxes as $box) {
echo $box->name . ': ' . $box->status . "\n";
}$paginated = HetznerStorageBox::storageBoxes()->paginate(25, 2);
$boxes = $paginated->items; // StorageBoxCollection
$meta = $paginated->pagination; // PaginationMeta DTO
echo "Page: " . $meta->page . " of " . $meta->lastPage;// Create a new storage box
$response = HetznerStorageBox::storageBoxes()->create([
'name' => 'backups-prod',
'storage_box_type' => 'bx11',
'location' => 'fsn1',
'password' => 'secure_password_here'
]);
$box = $response->storageBox;
$action = $response->action;
echo "Provisioned Storage Box ID: " . $box->id . "\n";
// Delete the storage box
$deleteAction = HetznerStorageBox::storageBoxes()->delete($box->id);
if ($deleteAction) {
echo "Deletion status: " . $deleteAction->status;
}// Reset Storage Box password
$action = HetznerStorageBox::storageBoxes()->resetPassword($boxId, 'new_secure_password_123');
echo "Action status: " . $action->status; // running / success
// Change delete protection
HetznerStorageBox::storageBoxes()->changeProtection($boxId, true);$folders = HetznerStorageBox::storageBoxes()->folders($boxId, '/backups');
foreach ($folders['folders'] as $folder) {
echo "Folder: " . $folder . "\n";
}Manage directories and access protocols for subaccounts.
$subaccountsManager = HetznerStorageBox::storageBoxes()->subaccounts($boxId);
// List subaccounts
$subaccounts = $subaccountsManager->all();
// Create a subaccount
$sub = $subaccountsManager->create([
'name' => 'upload-sub',
'description' => 'Subaccount for uploads',
'home_directory' => '/uploads',
'access_settings' => [
'samba_enabled' => false,
'ssh_enabled' => true,
'webdav_enabled' => true,
'read_only' => false
]
]);
// Reset subaccount password
$subaccountsManager->resetPassword($sub->id, 'SecurePassword123!');Manage point-in-time filesystem snapshots.
$snapshotsManager = HetznerStorageBox::storageBoxes()->snapshots($boxId);
// List snapshots
$snapshots = $snapshotsManager->all();
// Create a snapshot
$snapshot = $snapshotsManager->create([
'description' => 'Daily backup snapshot',
'labels' => [
'env' => 'production'
]
]);
// Delete a snapshot
$snapshotsManager->delete($snapshot->id);// Return a Guzzle Promise immediately
$promise = HetznerStorageBox::storageBoxes()->async()->all();
// Resolve promise when ready
$boxes = $promise->wait();// Execute multiple queries concurrently
$results = HetznerStorageBox::batch([
fn () => HetznerStorageBox::storageBoxes()->find(1),
fn () => HetznerStorageBox::storageBoxes()->find(2),
fn () => HetznerStorageBox::storageBoxTypes()->find(4),
]);
$box1 = $results[0];
$box2 = $results[1];
$type = $results[2];All exceptions inherit from GhostCompiler\Hetzner\StorageBox\Exceptions\HetznerException.
use GhostCompiler\Hetzner\StorageBox\Exceptions\AuthenticationException;
use GhostCompiler\Hetzner\StorageBox\Exceptions\ValidationException;
use GhostCompiler\Hetzner\StorageBox\Exceptions\RateLimitException;
use GhostCompiler\Hetzner\StorageBox\Exceptions\HetznerException;
try {
HetznerStorageBox::storageBoxes()->create(['name' => '']);
} catch (AuthenticationException $e) {
// 401 Unauthorized
} catch (ValidationException $e) {
// 422 Unprocessable Entity
$errors = $e->getErrors(); // Get field-specific validation errors
} catch (RateLimitException $e) {
// 429 Rate Limit Exceeded
$secondsToWait = $e->getSecondsUntilReset();
} catch (HetznerException $e) {
// Base exception handler
}Run PHPStan static analysis:
vendor/bin/phpstan analyseRun Psalm static analysis:
vendor/bin/psalmFormat code with Pint:
vendor/bin/pintBuilt using ServBay
- Mac M4 Tested
- macOS Apple Silicon
- Powered by ServBay

