jcergolj / metator-for-laravel
Laravel package that installs Deployer deployment scaffolding into an application.
Package info
github.com/jcergolj/metator-for-laravel
Language:Shell
pkg:composer/jcergolj/metator-for-laravel
Requires
- php: ^8.2
- deployer/deployer: ^8.0
- illuminate/console: ^12.0 || ^13.0
- illuminate/filesystem: ^12.0 || ^13.0
- illuminate/support: ^12.0 || ^13.0
- laravel/prompts: ^0.3
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-master
- v10
- v9
- v8
- v7
- v6
- v5
- v4
- v3
- v2
- v1
- dev-issue-106-deployer-capability-checks
- dev-issue-105-scaffolding-upgrade-guide
- dev-issue-104-deployer-interface-docs
- dev-issue-99-redis-site-isolation
- dev-issue-99-sqlite-extension-check
- dev-issue-83-sqlite-noop
- dev-issue-84-caddy-readiness
- dev-issue-85-permissions-noop
- dev-issue-86-ssh-order
- dev-issue-87-mariadb-rerun
- dev-issue-88-environment-noop
- dev-issue-90-preparation-noop
- dev-issue-90-base
- dev-fix/worker-sudoers-reruns
- dev-feat/optional-node-capability
- dev-fix/provisioning-confirmation
- dev-fix/provisioning-review
- dev-fix/activate-caddy-preparation
- dev-fix/verify-composer-preparation
- dev-fix/prepare-server-php-fpm
- dev-issue-60-release-gate-os-matrix
- dev-issue-51-refactor
- dev-issue-59-ubuntu-2604-support
- dev-issue-37-release-gate
- dev-issue-34-reconcile-disabled
- dev-issue-33-site-workers
- dev-issue-32-deployer-integration
- dev-issue-36-custom-steps-dns
- dev-issue-12-caddy-routing
- dev-agent/issue-35-environment
- dev-agent/issue-31-scheduler
- dev-agent/issue-30-redis
- dev-feat/29-local-mariadb
- dev-feat/28-github-deploy-key
- dev-feat/27-owned-sqlite-site
- dev-feat/26-ubuntu-php-baseline
- dev-feat/25-remote-provisioning
- dev-feat/minimal-deployer-recipe
- dev-feat/provisioning-behavioral-ci
- dev-feat/local-site-configuration
- dev-fix/9-production-environment-generation
- dev-fix/remaining-installation-bugs
- dev-fix/queue-worker-timeout
- dev-fix/preserve-dotenv-secrets
- dev-fix/restart-workers-in-scaffolding
This package is auto-updated.
Last update: 2026-10-24 15:33:42 UTC
README
Metator installs deployment scaffolding for Laravel applications. It combines Deployer with a metadata-driven server bootstrap pipeline.
Metator can prepare:
- PHP-FPM and CLI
- Caddy and optional Cloudflare DNS
- SQLite or site-owned local MariaDB
- Redis cache and runtime databases
- Scheduler and optional queue or Horizon workers
- GitHub deploy-key access
Metator prepares infrastructure. You run Deployer separately to release the application.
Requirements
Before installation, provide:
- A Laravel application with
.env.example - PHP 8.2 or newer locally
- Composer
- An already secured Ubuntu server
- Noninteractive SSH access with administrative
sudo - Git, Composer, PHP-FPM, systemd, and
sudoon the server
The supported server baseline is Ubuntu 24.04 with PHP 8.4 or 8.5. Basic OS hardening and operator SSH access are outside Metator's scope.
Quick Start
Install the package in your Laravel application:
composer require --dev jcergolj/metator-for-laravel php artisan metator:install
The installer creates:
metator.<name>.php: non-secret site configurationdeploy.php: editable Deployer recipescripts/: selected server bootstrap pipeline- A copy of the application's
.env.example
Then prepare, provision, and deploy:
php artisan metator:prepare-server --config=metator.production.php php artisan metator:provision --config=metator.production.php vendor/bin/dep deploy production
Provisioning does not deploy application code. Register the generated read-only GitHub deploy key when provisioning requests it.
Installation Choices
metator:install asks for the settings needed by one site:
- Site ID
- Repository and deployment branch
- SSH host and deployment user
- Domain
- PHP version
- SQLite or local MariaDB
- Redis, scheduler, and worker capabilities
- Deployment steps
The site ID is immutable and identifies the remote resources. It is not the repository name, domain, filename, or local checkout name.
The generated site configuration contains no secrets. Cloudflare credentials, when needed, are stored separately in an untracked local companion file.
Use --force only when deliberately regenerating an existing installation:
php artisan metator:install --force
Within scripts/steps/, forced regeneration replaces or removes only files
tracked in scripts/.metator-manifest.json. Untracked custom step files are
preserved.
Updating an existing installation
metator:install exits with an error if the selected site configuration already
exists, unless --force is supplied. In other cases, existing generated output
files are skipped without --force. Forced regeneration replaces the selected
site configuration, the generated deploy.php, bootstrap libraries and
instructions, and scripts/.env.example. Back up and compare customized
deploy.php content before restoring or merging application-specific tasks.
The selected-step manifest controls replacement of generated
scripts/steps/*.sh files. The package manifest controls updates to package-owned
files in metator/steps/; custom files in that local catalogue are preserved and
can be selected again. A forced install with Cloudflare deselected removes the
local metator.<name>.local.php companion file, so save any credentials you
still need before changing that selection.
For a deliberate scaffold upgrade:
- Back up the site configuration, local secret companion file, customized
deploy.php, custommetator/steps/files, and.env.production.local. - Update the package and run
php artisan metator:install --force, keeping the same configuration name and site ID for the existing site. - Compare the regenerated
deploy.phpwith the backup and merge required release-specific tasks. Confirm custom steps and local environment values are present before continuing. - Run
metator:prepare-serveronly if the shared baseline needs a capability or repair; then runmetator:provision --config=...for the selected site. - Review the readiness summary and run
vendor/bin/dep deploy productionseparately. Scaffolding installation itself makes no remote changes.
Forced regeneration changes local scaffolding only. Shared preparation, provisioning, and application deployment remain separate operator-run actions.
Server Workflow
Run the commands in this order:
- Install the local scaffolding.
- Prepare the shared server baseline when needed.
- Provision the selected site.
- Register the GitHub deploy key.
- Deploy the application with Deployer.
- Verify the URL, database, scheduler, and workers selected for the site.
prepare-server installs shared packages and capabilities. provision creates
only the selected site's resources and reuses the prepared baseline.
Who owns each operation
| Operation | Responsibility |
|---|---|
metator:prepare-server |
Prepare shared server capabilities such as PHP, Redis, Caddy, and Supervisor when the server needs them. |
metator:provision |
Configure the selected site's ownership, database and Redis allocations, GitHub access, web route, scheduler, worker program, permissions, custom steps, and readiness. It does not deploy application code. |
vendor/bin/dep deploy production |
Run the Laravel release lifecycle: fetch code, install Composer dependencies, migrate, optimize/cache, activate the release, clean old releases, and activate or restart only the selected site's workers. It does not prepare or provision the server. |
The generated deploy.php reads the selected site configuration for the site
identity, repository, SSH target, deployment user and path, PHP runtime, database
mode, and worker mode. The operator keeps the recipe editable for branch choice,
application builds, and other release-specific tasks. Metator's bootstrap
scripts remain responsible for infrastructure and site-owned resources.
The remote commands:
- Upload the exact local
scripts/directory - Use a fresh, site-owned staging directory
- Stream remote output locally
- Return the remote exit status
- Do not read from remote stdin
If SSH disconnects, inspect the server before retrying. The remote operation may have completed.
Deploying
Review deploy.php before the first deployment. It reads the selected site
configuration for the repository, SSH target, deployment path, PHP binary,
database mode, and worker lifecycle.
Deploy with:
vendor/bin/dep deploy production
The generated recipe verifies the provisioned PHP runtime before making release changes. It supports no workers, queue workers, or Horizon.
Custom deployment scripts
Add application-specific release work to the editable deploy.php. Define a
task, then attach it to a Deployer lifecycle task:
task('deploy:build-assets', function (): void { run('cd {{release_path}} && npm ci'); run('cd {{release_path}} && npm run build'); }); after('deploy:vendors', 'deploy:build-assets');
Select Install Node.js and npm during installation for server-side npm
builds. This records node => true in the site configuration. Provisioning
creates /var/www/<site-id>/shared/.npm, verifies access as the deployment user,
and leaves existing cache contents intact on reruns. The generated recipe sets
npm_config_cache for all Deployer commands, so both npm ci and npm run build
use this cache without requiring a writable deployment home. Sites without the
Node step do not get a cache or an npm requirement.
For an existing installation, regenerate and merge the scaffolding as described
above, then provision before deploying. When merging manually, update the generated
scripts/steps/11-node.sh, add 'node' => true to the site configuration, and copy
the recipe's npm_config_cache environment setting. Keep application-specific build tasks in
deploy.php; provisioning never installs application dependencies or builds assets.
Use:
{{release_path}}for files in the new release{{bin/php}}for the configured PHP binary- A hook before
deploy:symlinkfor required release files
For Tailwind:
task('deploy:build-tailwind', function (): void { run('cd {{release_path}} && {{bin/php}} artisan tailwindcss:download --force'); run('cd {{release_path}} && {{bin/php}} artisan tailwindcss:build --prod --no-tty'); }); after('deploy:vendors', 'deploy:build-tailwind');
If the Tailwind binary is already installed on the server, omit the download command. A failed task stops deployment before the new release is symlinked.
Verify generated files in the release directory:
test -f /var/www/<site-id>/releases/<release>/public/.tailwindcss-manifest.json find /var/www/<site-id>/releases/<release>/public/dist/css -name 'app-*.css' -type f
Keep deployment tasks in deploy.php. Do not add release commands to the
Metator server bootstrap scripts.
Custom Server Steps
Custom server steps are shell files in metator/steps/. Publish the catalogue
first if you want to inspect or edit the built-in steps:
php artisan vendor:publish --tag=metator-steps
Each custom step needs metadata and a matching function:
# @id: install-imagemagick # @title: Install ImageMagick # @group: none # @required: false # @default: false # @order: 65 step_install_imagemagick() { sudo apt-get install -y imagemagick }
Metadata rules:
@id: lowercase letters, numbers, and single hyphens@title: label shown by the installer@group: exclusive group, ornone@required:trueorfalse@default:trueorfalse@order: non-negative execution order
Available variables and helpers include APP_FOLDER, DOMAIN, PHP_VERSION,
PHP_PACKAGE_PREFIX, sudo, ok, warn, and die.
Custom steps must return a nonzero status on failure. The pipeline stops and reports the failed operation. Correct the step and rerun provisioning.
After adding a step:
php artisan metator:install
Select the custom step during installation. The generated scripts/steps/
directory is copied to the server and should not be edited there.
Optional Services
Web server
Caddy is the default web server and manages public HTTPS. Before provisioning:
- Point DNS to the server
- Allow inbound HTTP and HTTPS
- Ensure the configured domain resolves correctly
To use Nginx instead, add an Nginx step to metator/steps/ with the
web-server group, then select it and deselect Configure Caddy. Only one
web-server step may be selected.
Cloudflare DNS
Cloudflare DNS is optional. Leave Configure Cloudflare DNS unchecked when you do not want API credentials or DNS API calls.
Cloudflare does not replace Caddy. The web server and DNS choices are separate.
Workers and scheduler
Select only the capabilities used by the application:
- No workers
- Standard queue workers
- Horizon
- Scheduler
Worker and scheduler configuration is scoped to the site. Unrelated sites are not restarted when one site changes.
Multiple Sites
Multiple sites may use the same repository. Give each site a unique site ID.
For every site:
- Create a separate
metator.<name>.php - Use a unique site ID
- Register its generated GitHub deploy key
- Select its PHP, database, Redis, worker, and scheduler settings
- Provision and deploy separately
Prepare the server again only when a new shared capability is needed. Existing sites keep their configuration, credentials, data, deploy keys, and running work.
Updating Environment Values
For a new site, provisioning copies the application's current local .env,
preserving comments, layout, and application-specific values. It then applies
APP_ENV=production, APP_DEBUG=false, the site's HTTPS APP_URL, and the
provisioned database and Redis settings/prefixes. It generates a separate
production APP_KEY rather than reusing the local key.
Local DB_URL is cleared so it cannot override the provisioned database. When
using MariaDB, local DB_SOCKET is also cleared to use the provisioned TCP host.
When using provisioned Redis, local Redis URLs and credentials are cleared as well.
New site environments also receive capability-based defaults:
| Setting | Production value |
|---|---|
| Database | SQLite's shared database path, or local MariaDB host, port, site-owned database/user, and generated password |
| Cache and sessions | file without Redis; redis when Redis is selected |
| Queue | sync without workers; database for standard workers; redis with the Redis queues capability |
| Connection routing | Database queues use the selected database; Redis cache uses cache, while sessions, queue jobs, and cache locks use default |
| Session cookies | Secure, host-only, with a unique metator_<site-id>_session name |
| Logging | LOG_LEVEL=info |
Database-backed workers require the application's queue tables to be migrated through the normal deployment lifecycle. These defaults do not guess mail, filesystem, broadcasting, or third-party service credentials.
The local .env is required when running metator:provision. It is transferred
through temporary, permission-restricted staging, not written into generated
scaffolding. The generated scripts/.env.example is not the provisioning input.
Ordinary reruns preserve remote application values and do not merge local changes.
Review application-specific values before the first deployment: local mail, storage, payment, and other service credentials may not be suitable for production. Provisioning does not open an editor or wait for input on the server.
Put application-specific production values in a Git-ignored local dotenv file:
php artisan metator:update-env \ --config=metator.production.php \ --input=.env.production.local
Metator preserves its managed identity, runtime, database, Redis, and key values. It does not refresh Laravel's cached configuration or restart workers; use the normal Deployer and application lifecycle for those actions.
Retries and Changes
An unchanged provisioning rerun verifies existing resources without:
- Rotating credentials or keys
- Rewriting configuration
- Reloading shared services
- Restarting unrelated workers
- Duplicating cron entries
If provisioning fails, it returns a nonzero status and does not report the site as ready. Existing site-owned resources are preserved for recovery.
Metator rejects unmanaged ownership conflicts and ordinary PHP or database engine changes. Resolve those explicitly instead of deleting site data.
Troubleshooting
Missing shared capability
Run metator:prepare-server for a configuration that selects the capability,
then rerun metator:provision.
Provisioning failed
Use the step summary and remote error as the source of truth. Inspect the server before retrying.
Ownership conflict
Keep the existing resource unchanged. Resolve the site ID or conflicting managed resource manually; Metator will not adopt unmarked resources.
HTTPS unavailable
Check DNS and inbound HTTP/HTTPS access. Caddy obtains the certificate only when the configured domain resolves correctly.
Deployment failed
Review:
deploy.php- GitHub deploy-key registration
- The selected PHP binary
- The application's
.env - Custom deployment tasks
Provisioning and deployment are separate operations, so rerun Deployer after fixing the deployment issue.
Resource Ownership
Metator manages site-owned resources including:
- Site directory and metadata
- Shared environment
- Database and MariaDB credentials
- Redis allocations
- GitHub deploy key and SSH block
- Caddy route
- Scheduler entry
- Worker configuration
Metator does not provide a full uninstall command or delete site data. Stop the site lifecycle, archive required data, confirm no other site uses the resources, and remove them manually. Shared PHP, MariaDB, Redis, cron, Supervisor, and Caddy services remain in place.