aschmelyun/fleet
Fleet is a lightweight Laravel package for deploying applications to your own servers with Docker. It offers simple Artisan commands to provision a server, build and ship containers, and manage deployments without complex infrastructure tooling.
composer require <vendor>/<package> — Laravel's package discovery registers the service provider automatically; no manual config/app.php edit needed on Laravel 9+.php artisan vendor:publish --tag=<package>-config. Check config/<package>.php for paths, binary locations, and timeouts.composer.json constraints. v1.1.1's only change is a bump of symfony/yaml and symfony/process (fix for #22). Confirm your app doesn't pin older majors:
composer why symfony/yaml
composer why symfony/process
If another dependency (test tooling, deploy scripts, older Symfony components) requires <5.4, the bump will stall the update — resolve before upgrading.$definition = Yaml::parseFile(base_path('deploy.yaml'));
$process = new Process(['git', 'rev-parse', '--short', 'HEAD']);
$process->mustRun();
Wrap YAML parsing behind a typed DTO. YAML parses to nested arrays with no schema. Map it once into a value object or config repository and validate there, rather than passing raw arrays through the app. This keeps symfony/yaml an internal detail and makes future parser swaps painless.
Bind it in the container. Register a singleton that resolves the config path and cache the parsed result per request:
$this->app->singleton(DeploymentDefinition::class, fn () => DeploymentDefinition::fromYaml(
Yaml::parseFile(config('deploy.manifest'))
));
Long-running processes belong in jobs, not controllers. The symfony/process side of this package will block the request worker. Dispatch a queued job, give it a timeout slightly below retry_after, and stream output to a log or broadcast channel.
Prefer array-form commands. new Process(['bin/console', 'cache:clear']) avoids shell quoting entirely; the string form re-introduces escaping bugs on paths with spaces.
Use the package's Artisan surface for day-to-day work and its underlying services for programmatic cases — that keeps you on the supported path and makes behaviour identical between CLI and scheduled runs.
Test with fakes, not real subprocesses. Assert on the command array and mock the runner; only integration-test against the real binary in a dedicated suite. Tests that shell out are the usual cause of flaky CI on this kind of package.
Yaml::parse() after upgrading and diff the output before shipping.symfony/yaml is not yaml_parse(). The PECL extension and Symfony's parser diverge on booleans (yes/no/on), timestamps, and !!php/object. Always parse with the parser the package actually uses.Yaml::PARSE_DATETIME, PARSE_CONSTANT, and PARSE_CUSTOM_TAGS are off by default; values that look like dates silently become strings. Decide explicitly and document it.!include tags throws ParseException — catch it and surface the file plus line number, or the error is opaque in production.eval or unserialize parsed YAML. YAML from an untrusted source is an object-injection vector; keep manifests in the repo, not in user uploads.$process->getExitCode() / use mustRun(). Some tools write errors to stdout, so grepping output is unreliable.setTimeout(60) plus setIdleTimeout(15); the default is 60s and a hung child can pin a queue worker indefinitely.$process->getErrorOutput() and $process->getExitCodeText() are more useful than the exception message. Log both, plus the command array, on failure.Process inherits the parent env by default. Pass an explicit env array for anything touching credentials, and remember setWorkingDirectory() — relative paths in the child resolve against it, not your app root.How can I help you explore Laravel packages today?