Breaking Changes

Custom VHost Configuration

In v4.x the vhost template engine changed from Blade to Mustache. If you have customised the default vhost template, your existing customisations will not carry over, and any Blade syntax (@if, {{ $variable }}, etc.) will no longer be interpreted. See the Mustache manual for the new syntax.

To avoid breaking live sites during the upgrade, vhost generation is disabled by default on every existing site. Each site needs you to review and enable the new template before Vito will regenerate its vhost.

For each site, open its Settings page to see the current vhost alongside a preview of the new Mustache-generated output. Once you are happy that the new template produces the configuration you expect, enable it to switch the site over.

NodeJs Sites

The original NodeJs site type is now deprecated. Existing NodeJs sites will continue to work, but the type can no longer be used to create new sites.

Use the new Node site type instead. It uses Site Tooling to manage Node versions and any other tools your site needs, rather than baking the version into the site type.

There is no automated migration from the deprecated type to the new one. If you want the new features on an existing NodeJs site, you will need to migrate it manually.

VitoAgent updated

The vitoagent service has been updated for v4.x, and if you are running this, you will need to update it via the services for each server. You can do this easily by uninstalling and reinstalling the VitoAgent on each server it's currently installed on.

Where a VitoAgent is not updated on a given server, certain monitoring statistics will appear empty of have no value and restart monitoring of the server will not function.

Database Backup Format

In v4.x, database backups are streamed and stored as compressed .sql.gz archives. Previously (v3.x) they were stored as .zip archives.

Database backups created in v3.x (.zip) cannot be restored in v4.x. v4 can only restore database backups that were created in v4 (.sql.gz). After upgrading, take a fresh database backup so you have a restorable v4 backup, and keep your old v3 .zip archives around only if you have a separate way to restore them.

This only affects database backups. File/directory backups are unaffected — they continue to use the .tar.gz format and restore as before.

Plugins

If you maintain a Vito plugin, these are the contract changes you need to be aware of in v4.x. The plugin registration API (App\Plugins\Register* builders, AbstractPlugin, PluginInterface) and the core ServiceInterface / AbstractService base classes are unchanged. Every breaking change below lives in the contracts that specific provider types extend.

Who this affects: plugins registering a Site Type, Web Server service, or Source Control provider. Plugins that only register Server providers, Storage providers, DNS providers, Notification channels, Workflow actions, Server/Site features, or Commands are unaffected.

At a glance

Your plugin registers...Action needed
Site typeYes - move vhost() to vhostTemplate() + vhostData(); fix the progress() signature
Web serverYes - implement generateVhost() + deploySplash(); update updateVHost(); drop changePHPVersion()
Source controlOnly if you implement the interface directly (not via the abstract)
Anything reading $site->user / $site->ssh_key / FPM poolsReview - these are now isolated-user-backed
Server provider, Storage, DNS, Notifications, Workflow, Features, CommandsNone

Site Types

The App\SiteTypes\SiteType interface was reworked. The vhost rendering model changed from "return markup" to "point at a template and supply data".

Removed

PHP
// GONE from the SiteType interface
public function vhost(string $webserver): string|View;

Added (required on the interface):

PHP
public function vhostData(): array;                 // data passed to the vhost template
public function supportedWebservers(): ?array;      // null = all
public function vhostTemplate(string $webserver): ?string;
public function deploymentEnvironment(): array;
public function afterDeploy(Deployment $deployment): void;
public function defaultDeploymentScript(): string;

Migration

  • Move your markup into a Blade template, return its name from vhostTemplate(), and supply variables via vhostData().
  • defaultDeploymentScript() now defaults to reading resources/deployment-scripts/{id}.sh.
  • The protected progress() signature changed from progress(int $percentage) to progress(int $percentage, ?string $step). Overrides or callers must add the $step argument. It now also broadcasts a site.updated socket event and writes the new progress_step column.

If you extend AbstractSiteType you will not get a fatal error, because the abstract supplies defaults for all six new methods. But your old vhost() method becomes dead code that is never called, so your site's vhost silently falls back to defaults until you migrate it.

New optional tooling hooks (default to "no tooling"): static createTimeTools(): array, static requiredTooling(): array, static supportsTooling(): bool.

Web Server services

The App\Services\Webserver\Webserver interface changed significantly, and AbstractWebserver gained two new abstract methods you must implement (or you get a fatal error):

PHP
abstract public function generateVhost(Site $site, ?string $template = null): string;
abstract public function deploySplash(): void;

The updateVHost() signature changed, dropping the block-manipulation arrays. Note the default $restart also flipped from true to false, and the protected helper getUpdatedVHost() was removed.

PHP
// 3.x
public function updateVHost(Site $site, ?string $vhost = null, array $replace = [], array $regenerate = [], array $append = [], bool $restart = true): void;
// v4.x
public function updateVHost(Site $site, ?string $vhost = null, bool $restart = false): void;

changePHPVersion(Site $site, string $version): void was removed from the interface and the entire codebase. PHP-version changes are no longer driven through the web server.

New SSL-related interface methods (defaults provided by AbstractWebserver, so extending it is safe): createsSiteSSLs(), siteDefaults(), canConfigureSSL(), allowedSslMethods(), defaultSslMethod(). These back a reworked SSL system using a new App\Enums\SslMethod enum and a new SslType::CSR case. createsSiteSSLs() reads the creates_site_ssls flag from your service's data config.

Source Control Providers

The SourceControlProvider interface gained four new methods; AbstractSourceControlProvider supplies safe defaults (SSH port 22, no editable fields).

PHP
public function getSshPort(): int;
public static function editableFields(): array;
public function editRules(array $input): array;
public function editData(array $input): array;

Migration

  • Extend AbstractSourceControlProvider and you are safe.
  • Implement SourceControlProvider directly and you must add all four methods, or you get a fatal error.

Security contract: if you override editableFields() to return a non-empty list, you must also override editData() to whitelist exactly which keys are merged. The default spread would let callers overwrite encrypted fields like token.

RegisterSourceControl is additive and non-breaking: new ->connectable(bool) and ->usableForSites(bool) builders, and the registry now also emits connectable, usable_for_sites, and an auto-derived editable_fields. v4.x also ships a built-in GitHub App provider.

Isolated Users

This affects any plugin that touches a Site. v4.x introduces a dedicated App\Models\IsolatedUser model, and the Site's identity attributes are now relation-backed.

  • $site->user and $site->ssh_key are now accessors that fall back to $site->isolatedUser, and the underlying columns may be null. Reads still work, but writing directly to them no longer behaves as it did.
  • Site::changePHPVersion() and Site::activeSsl() were removed.
  • New helpers: Site::fpmPoolSharedWithSiblings(), userSharedWithSiblings(), siblingsSharingUser(), htpasswdPath(), plus tooling helpers and a progress_step column. Site now eager-loads isolatedUser by default.
  • FPM pools and the isolated user are now shared across sibling sites of the same user, and isolation runs under a lock. Plugins that created or assumed one FPM pool per site should account for this.
  • config/core.php adds a reserved_user_names blocklist enforced during site creation.

New extension points and built-ins

These are additive, so no action is required.

  • Tooling system: App\Tooling\ToolingInterface plus AbstractTooling / AbstractMiseTooling, registered via config('tooling.providers') (an array of class strings; there is no RegisterTooling plugin builder yet). Built-ins: Node, Bun, Yarn, Pnpm.
  • New built-ins: SFTP storage provider, Valkey database service, NodeSite / BunSite site types, and an AbstractProxiedSiteType base class.
  • WebSocket config moved into config/core.php (ws_host, ws_port, ws_broadcast_secret, ws_allowed_origins). The SocketEvent::dispatch() API itself is unchanged.