Complete coverage
Every Hetzner Cloud endpoint, from actions and billing to volumes — never a convenient subset.
[ OPEN SOURCE ]
A Laravel SDK for Hetzner Cloud that covers the whole API, types every response, manages several projects at once, and fakes cleanly in tests.
Hetzner is where a great deal of European infrastructure quietly lives, and its API is good. What was missing was a Laravel-shaped way into it: a service provider, a facade, real types on the way back, and a way to test against it without touching a live account.
It covers every resource the API exposes rather than a convenient subset — actions, billing, certificates, DNS zones, firewalls, floating IPs, images, ISOs, load balancers, locations, networks, placement groups, primary IPs, servers, SSH keys and volumes. A partial SDK is worse than none: the moment you reach the resource it skipped, you are back to raw requests, and now there are two ways of talking to the same API in one codebase.
Version 1.0.3 adds Laravel 13 and multiple projects. Every Hetzner token belongs to one project, so the SDK now holds one per project in config, switches with project('staging'), and takes a token known only at runtime with withToken() — the shape you need when each customer or team brings their own Hetzner account. It also tracks the API as it moves: network members, DNS record comments, server locations now that datacenters are gone, and deprecation details on server types and images.
[ OPEN SOURCE · v1.0.3 ]
An elegant, type-safe SDK for Hetzner Cloud: servers, DNS, networks, load balancers and every other resource the API exposes. Now with Laravel 13 and multiple projects.
$ composer require boci/hetzner-laraveluse Boci\HetznerLaravel\Facades\HetznerLaravel;
$response = HetznerLaravel::servers()->create([
'name' => 'my-server',
'server_type' => 'cpx22',
'image' => 'ubuntu-24.04',
'location' => 'nbg1',
]);
$server = $response->server();
echo $server->name(); // my-server
echo $server->status(); // initializinguse Boci\HetznerLaravel\Facades\HetznerLaravel;
// The default project, from HETZNER_TOKEN
$servers = HetznerLaravel::servers()->list();
// Another project from config/hetzner-laravel.php
$staging = HetznerLaravel::project('staging')->servers()->list();
// A token only known at runtime, e.g. one per team
$theirs = HetznerLaravel::withToken($team->hetzner_token)
->servers()
->list();use Boci\HetznerLaravel\Facades\HetznerLaravel;
HetznerLaravel::dnsZones()->create([
'name' => 'example.com',
'mode' => 'primary',
'ttl' => 3600,
]);
HetznerLaravel::dnsZones()->rrsets()->create('example.com', [
'name' => 'www',
'type' => 'A',
'ttl' => 3600,
'records' => [['value' => '192.168.1.1']],
]);use Boci\HetznerLaravel\Testing\ClientFake;
use Boci\HetznerLaravel\Responses\Servers\CreateResponse;
$client = new ClientFake([
CreateResponse::fake(['name' => 'test-server']),
]);
$response = $client->servers()->create(['name' => 'test-server']);
expect($response->server()->name())->toBe('test-server');
// No real server was created
$client->assertSent(Servers::class, fn ($method) => $method === 'create');Every Hetzner Cloud endpoint, from actions and billing to volumes — never a convenient subset.
One token per project in config, project('name') to switch, withToken() for tokens stored per team.
Full PHP 8.2+ types on requests and responses, so an IDE can tell you what came back.
A ClientFake with assertSent(), so a test that provisions a server does not provision a server.
Rate limits and request details come back alongside the data rather than being thrown away.
Service provider and facade for Laravel 12 and 13, registered on install. Nothing to wire up.
Issues and pull requests are welcome, and so is a conversation about building one for another platform.
GET IN TOUCH