beziworld / laravel-directadmin
Laravel client for the DirectAdmin API: users, domains, e-mail, databases, DNS, SSL, cron and backups.
Requires
- php: ^8.3
- ext-json: *
- guzzlehttp/guzzle: ^7.8
- illuminate/console: ^12.0|^13.0
- illuminate/contracts: ^12.0|^13.0
- illuminate/http: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^3.7|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
- phpstan/phpstan-strict-rules: ^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-04 16:36:26 UTC
README
Laravel client for the DirectAdmin hosting control panel: accounts, packages, domains, e-mail, databases, FTP, DNS, cron, SSL, backups and server information, for one or many servers.
Features
- Multiple named connections (like database connections) plus runtime connections for servers stored in your DB
- Impersonation (
admin|user) per connection or per call withloginAs() - Topic resources:
users,packages,domains,email,databases,ftp,dns,cron,ssl,backups,system - Legacy
CMD_API_*commands in JSON mode and the new/api/*JSON API; rawcommand(),action()andapi()for anything not covered - Reliable error detection: DirectAdmin reports some failures with HTTP 200 and others with HTTP 500, and failed logins as 401, redirects or HTML pages; all of them become typed exceptions
- No retries after an authentication failure, so the Brute Force Monitor does not block your server
php artisan directadmin:checkto test a connection- PHP 8.3+, Laravel 12 and 13, PHPStan level 8
Installation
composer require beziworld/laravel-directadmin
php artisan vendor:publish --tag=directadmin-config # optional
DIRECTADMIN_HOST=https://server.example.com:2222 DIRECTADMIN_USERNAME=admin DIRECTADMIN_PASSWORD=login-key # a login key is strongly recommended over the password DIRECTADMIN_VERIFY_SSL=true # false or a CA bundle path for self-signed panels
php artisan directadmin:check
Create a login key in DirectAdmin (Login Keys), restrict it to your application's IP address and to the commands
you use, and deny CMD_LOGIN_KEYS on the key itself.
Usage examples
Provision a hosting account after an order is paid
use Beziworld\DirectAdmin\Data\AccountRequest; use Beziworld\DirectAdmin\Exceptions\CommandException; use Beziworld\DirectAdmin\Facades\DirectAdmin; class ProvisionHosting implements ShouldQueue { public function handle(): void { $password = Str::password(16); try { DirectAdmin::users()->create(new AccountRequest( username: $this->order->username, email: $this->order->email, password: $password, domain: $this->order->domain, package: $this->order->plan->package, // DirectAdmin package name ip: config('hosting.shared_ip'), )); } catch (CommandException $e) { // e.g. "DirectAdmin CMD_API_ACCOUNT_USER failed: Unable to Create User (That username already exists)" $this->fail($e); return; } $client = DirectAdmin::loginAs($this->order->username); // act as the new user $client->ssl()->requestLetsEncrypt($this->order->domain); $client->email()->createAccount($this->order->domain, 'office', Str::password(16), quota: 2048); Mail::to($this->order->email)->send(new HostingReady($this->order, $password)); } }
Suspend unpaid accounts
$overdue = Subscription::overdue()->pluck('username')->all(); if ($overdue !== []) { DirectAdmin::users()->suspend(...$overdue); } // After payment: DirectAdmin::users()->unsuspend('john');
Upgrade a plan or change limits
DirectAdmin::users()->changePackage('john', 'business'); // Custom limits; current settings are read first, because DirectAdmin turns omitted checkboxes OFF. DirectAdmin::users()->customize('john', ['quota' => 20480, 'bandwidth' => 'unlimited', 'ssh' => true]);
Usage dashboard for a customer
$usage = DirectAdmin::users()->usage('john'); // ['quota' => '512.3', 'bandwidth' => '1024', ...] $config = DirectAdmin::users()->config('john'); // ['package' => 'business', 'quota' => '20480', ...]
$email = DirectAdmin::loginAs('john')->email(); $email->createAccount('example.com', 'office', $password, quota: 1024, sendLimit: 500); $email->updateAccount('example.com', 'office', password: $newPassword); $email->createForwarder('example.com', 'sales', ['anna@example.com', 'piotr@example.com']); $email->createAutoresponder('example.com', 'office', 'We will reply within 24 hours.'); $email->createVacation('example.com', 'anna', 'On holiday until 2 January.', new DateTime('2026-12-24'), new DateTime('2027-01-02')); $email->setCatchAll('example.com', ':fail:');
Databases for a deployment
$db = DirectAdmin::loginAs('john')->databases(); $db->create('shop', 'shop', $password); // creates john_shop and user john_shop $db->addAccessHost('john_shop', '10.0.0.%'); // remote access from your app servers $db->changeUserPassword('john_shop', 'john_shop', $rotated);
DNS records (e.g. domain verification)
$dns = DirectAdmin::loginAs('john')->dns(); $dns->add('example.com', 'TXT', 'example.com.', '"google-site-verification=abc123"'); $dns->add('example.com', 'MX', 'example.com.', '10 mail.example.com.'); $dns->update('example.com', 'A', 'www', oldValue: '10.0.0.1', newValue: '10.0.0.2', ttl: 300); $dns->delete('example.com', 'TXT', 'example.com.', '"google-site-verification=abc123"');
Cron, FTP, SSL and backups
$client = DirectAdmin::loginAs('john'); $client->cron()->create('* * * * *', '/usr/local/bin/php /home/john/app/artisan schedule:run'); $client->ftp()->create('example.com', 'deploy', $password, type: 'custom', path: '/home/john/domains/example.com/app'); $client->ssl()->requestLetsEncrypt('example.com', ['example.com', 'www.example.com', 'shop.example.com']); $client->backups()->create('example.com', ['database', 'email']);
Several servers
// config/directadmin.php 'connections' => [ 'default' => [...], 'server2' => ['host' => env('DA2_HOST'), 'username' => env('DA2_USER'), 'password' => env('DA2_KEY')], ], DirectAdmin::connection('server2')->users()->all(); // Servers stored in the database DirectAdmin::build([ 'host' => $server->host, 'username' => $server->username, 'password' => decrypt($server->login_key), ])->system()->info();
Anything else
DirectAdmin::command('SHOW_USER_DOMAINS', ['user' => 'john']); // any CMD_API_* (GET) DirectAdmin::action('DOMAIN', ['action' => 'modify', 'domain' => 'x.com', 'php' => 'ON']); // any CMD_API_* (POST) DirectAdmin::api('GET', 'db-show/databases'); // the /api JSON API DirectAdmin::system()->version(); // /api/version
Exceptions
All exceptions extend DirectAdminException:
| Exception | When |
|---|---|
AuthenticationException |
Credentials rejected (401, redirect, HTML login page). Never retried. |
CommandException |
DirectAdmin reported an error (command(), details(), httpStatus(), payload()) or could not be reached |
InvalidConfiguration |
Unknown connection or missing host / credentials |
Notes
- Resource methods that create objects take names without the account prefix (
create('shop')createsjohn_shop); methods referencing existing databases take the full name. - Backups and restores are queued by DirectAdmin; the call only confirms scheduling.
- Every call is synchronous and some (account creation, SSL) take several seconds: run them in queued jobs.
Development
composer install
composer ci # pint --test, phpstan (level 8), pest
Author
Created and maintained by Kamil Ćwiertnia, beziworld.eu.
- E-mail: biuro@beziworld.eu
- Issues and feature requests: GitHub issues
- Security vulnerabilities: see SECURITY.md
Building a hosting panel, automated provisioning or a custom Laravel project? Get in touch through beziworld.eu.
Other packages by the author
- beziworld/laravel-przelewy24: Przelewy24 payments
- beziworld/laravel-tpay: Tpay payments and BLIK
- beziworld/laravel-smsapi: SMSAPI messages and notifications
License
MIT. See LICENSE.md. This package is not affiliated with JBMC Software (DirectAdmin).