Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Vesta Api Laravel Package

tabuna/vesta-api

Laravel/PHP client for the Vesta Control Panel API. Manage users, domains, DNS records, mail accounts, databases, and backups from your application through a clean, expressive wrapper with a simple, consistent interface.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the client and wire up credentials in three steps:

composer require tabuna/vesta-api:^2.0
php artisan vendor:publish --provider="Tabuna\VestaAPI\Providers\VestaServiceProvider"

Then populate .env:

VESTA_HOST=panel.example.com
VESTA_PORT=8083
VESTA_USER=admin
VESTA_PASSWORD=secret
VESTA_SSL=true

The package exposes a facade (Tabuna\VestaAPI\Facades\Vesta) and an injectable client. Your first use case is almost always read-only: verify connectivity and your credentials before attempting writes.

use Tabuna\VestaAPI\Facades\Vesta;

// Smoke test: list users on the panel
$response = Vesta::user()->list();

if ($response->failed()) {
    report($response->message());
}

Before you write any code, check the installed version's composer.json constraints. Release 2.0 is advertised as "Updated to Laravel 7x" — a framework-compatibility bump, not a feature release — so the illuminate/* and php requirements in the package are the first thing to reconcile against your app's composer.json. If you're on Laravel 9+, plan for a fork (see Gotchas) before promising a delivery date.

Look first at:

  • config/vesta.php – host/port/user/password and SSL toggle.
  • The facade accessor and the per-command "section" classes (user(), domain(), database(), dns(), etc.) – each maps 1:1 to a VestaCP shell command (v-list-users, v-add-domain, …).
  • Return objects – they typically expose successful() / failed() plus the raw JSON body.

Implementation Patterns

Provision a customer in one flow. Chain the section helpers to mirror the steps you'd take in the VestaCP UI. Keeping the sequence under a single try/catch means the whole provisioning op is atomic enough for most use cases:

use Tabuna\VestaAPI\Facades\Vesta;

try {
    Vesta::user()->add([
        'username' => $username,
        'password' => $password,
        'email'    => $email,
        'package'  => 'default',
    ]);

    Vesta::domain()->add([
        'user'   => $username,
        'domain' => $domain,
        'ip'     => '203.0.113.10',
    ]);

    Vesta::database()->add([
        'user'     => $username,
        'database' => $database,
        'dbuser'   => $dbUser,
        'dbpass'   => $dbPass,
    ]);
} catch (\Throwable $e) {
    // compensate: roll back the user so retries are clean
    Vesta::user()->delete(['username' => $username]);
    throw $e;
}

Wrap the client behind your own service. Because the package is small and its return shapes are thin, don't let raw Vesta::… calls leak into controllers or jobs. Create a HostingProvisioner class that depends on the client, translate the package's response into your own result/value objects, and inject it. That single seam makes it trivial to swap in a direct HTTP client later if (when) you need to fork or replace the package — and since 2.0 shipped no API changes, that seam is still your only real upgrade insurance.

Push slow work onto the queue. v-add-domain, v-rebuild-web-domains, and DNS changes are real shell commands with timeouts. Dispatch a ProvisionDomainJob, call the client inside it, and let retries + backoff() handle transient panel hiccups. Do not run these in a synchronous request handler.

Listen to outcomes. Fire a domain event (DomainProvisioned, PanelCommandFailed) from your service wrapper so other parts of the app – mail setup, certificate issuance, billing state – react without a hard dependency on VestaCP.

Bulk data: Vesta::user()->list() and friends return the panel's dataset as an array. Cache it (Cache::remember('vesta.users', 600, …)) rather than polling the panel from dashboards; VestaCP isn't built for high query volume and rate-limiting you is entirely plausible.

Read before write. Before deleting or suspending, re-query the object. VestaCP has no transactions – if your app thinks a user exists when it doesn't, you'll get opaque status codes back from the API instead of a clear 404.

Gotchas and Tips

2.0 is a compatibility bump, not a revival. The release notes consist of a single line: "Updated to Laravel 7x." That tells you two things. First, the previously documented "last release April 2020" is now out of date — there is a maintained-ish release line again, so ^2.0 is the version to pin rather than 1.x. Second, the ceiling is Laravel 7: Laravel 7 itself reached end-of-life in 2021, and there is no indication this package supports Laravel 9/10/11/12. If your app is on ≥ 9, you are still forking. Check composer require output against your lockfile — dependency resolution, not the changelog, is the authoritative compatibility statement.

It's a major version bump with no documented breaking changes. Semver says 1.x → 2.0 may break you; the release notes say nothing about the public API. Diff the tagged source against your installed 1.x (or run your test suite against ^2.0 in a branch) instead of trusting the single-line note. The most likely real change is a raised illuminate/* floor plus associated type-hint/return-type adjustments — cheap to absorb, expensive to discover in production.

VestaCP's API is not REST. It answers everything with 200 OK and encodes the real outcome in a status value inside the body (negative/1 for failure, positive/0 for success depending on the command). Always check ->successful()/->failed() on the response object – never rely on HTTP codes. This behaviour is a property of the panel, not the package, so 2.0 does not change it.

TLS on port 8083 breaks silently. VestaCP ships with a self-signed cert. In older Guzzle you could set verify => false; here you control that via config (VESTA_SSL). For production, install a real cert on the panel or set verify => '/path/to/ca.pem'. Silently disabling verification "just to get it working" is how you end up shipping an unauthenticated admin channel.

Credentials with shell-special characters in .env. VestaCP passwords regularly contain $, #, or backticks. Laravel's .env parser will happily mangle those – quote them (VESTA_PASSWORD="p@ss\$word") and confirm the value your app actually sees (config('vesta.password')) before blaming the API.

Return shapes are the panel's, not yours. Command output is a JSON blob keyed by arbitrary field names that differ per command (v-list-user vs v-list-database). Type them yourself in your service layer; don't spread $response['data']['DOMAIN'] style access across your codebase.

Error messages are lossy. A failed v-add-domain often comes back as just "status": -2 or a raw shell error string. Map codes to friendly errors in your wrapper, and log the full response body (Log::debug(json_encode($response))) at failure time – you'll need it later.

Idempotency is on you. VestaCP has no upsert; adding an existing domain returns an error. Guard writes with an existence check (Vesta::domain()->list() filtered) or catch the specific "already exists" status and treat it as success in your provisioner.

Extension point of last resort: if a command isn't wrapped, drop to the raw client's generic request()/command() method and pass the VestaCP verb (v-add-cron-job, etc.) plus its arguments. That's also your escape hatch when a wrapper method has a bug — the 2.0 notes don't claim bug fixes, so the raw call remains the reliable path for anything the wrappers get wrong.

Still audit before using in a security-critical, multi-tenant flow. A Laravel 7 compatibility bump is not a security review. The package is a thin wrapper over Guzzle (~200 lines); read the credential handling and request signing yourself, or fork it. That remains the cheapest long-term answer, and 2.0 doesn't change the calculus — it just means your fork now has a fresher starting point.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle