rch/capistrano-bundle
Symfony bundle that packages Capistrano deployment recipes and configuration for PHP applications. It helps automate releases, shared files, and remote server tasks with customizable stages, roles, and deployment workflows.
Read this first: the package is for Symfony2+, not Laravel. rch/capistrano-bundle is a Symfony kernel bundle that wraps a set of Capistrano deployment recipes. It is not a Composer package you can composer require into a Laravel app and get value from — Laravel has no Symfony bundle registration, so the PHP bundle layer is dead weight. What is actually reusable is the recipe structure (Capfile + config/deploy.rb + stage files) and the Symfony-specific tasks you can translate to Artisan commands.
Treat it as a reference implementation, not a dependency.
Minimal steps for a Laravel developer:
composer require rch/capistrano-bundle. Clone or browse the repo and read the Resources/ directory for the Capfile, deploy.rb, and stage templates.gem install capistrano.Capfile, config/deploy.rb, config/deploy/production.rb, config/deploy/staging.rb.app/console cache:clear, doctrine:migrations:migrate, assetic:dump) as their Artisan equivalents (php artisan optimize:clear, php artisan migrate --force, npm ci && npm run build).:repo_url and :deploy_to at your Git remote and target path, then cap production deploy:check before your first real deploy.First use case: a single-server, release-symlink deployment with shared .env, shared storage/, Composer install, frontend build, and artisan migrate --force. That is the 90% case for a Laravel app, and the package's task ordering (install → build → migrate → publish → symlink) gives you a serviceable skeleton to start from.
Copy, don't depend. The most defensible pattern is to vendor the recipes into your repo under deploy/ and own them. Pin your Capistrano gem version in a Gemfile so CI and local runs match.
Stage-per-environment. Keep config/deploy/staging.rb and config/deploy/production.rb separate, with different :branch, :deploy_to, and server roles. Override only what differs; keep shared settings in deploy.rb.
Shared state is the whole game. Declare set :linked_files, %w[.env] and set :linked_dirs, %w[storage/app storage/logs storage/framework/cache storage/framework/sessions storage/framework/views public/storage]. Without these, every release boots with an empty storage/ and no environment file, which is the single most common cause of a broken first deploy.
Translate Symfony hooks to Laravel hooks at the same lifecycle points the bundle uses:
| Bundle (Symfony) | Laravel equivalent | Capistrano hook |
|---|---|---|
doctrine:migrations:migrate |
php artisan migrate --force |
after :updated |
assetic:dump / assets:install |
npm ci && npm run build |
after :updated |
cache:clear --env=prod |
php artisan optimize:clear (or optimize in CI) |
after :updated |
cache:warmup |
php artisan config:cache && php artisan route:cache |
before :publishing |
| permissions fix | chown -R deploy:www-data storage bootstrap/cache |
after :finished |
CI-driven deploys. Trigger cap production deploy from a GitHub Actions job after tests pass. The pattern the bundle implies (deploy from a trusted machine via SSH, not from a webhook handler) is exactly what you want for Laravel too — it avoids the security surface of a self-hitting deploy endpoint.
Role-based servers. If you grow beyond one box, use role :app, role :web, role :db, role :worker and scope tasks with on roles(:app). Laravel queue workers get their own role and a after :published restart hook (php artisan queue:restart).
NOASSERTION license. Do not put this in a dependency graph you have to maintain; treat it as a read-only reference. Have someone check the license text before copying recipe files verbatim into a commercial repo.cap deploy for Laravel, this is not it.Gemfile/Gemfile.lock alongside composer.json. That is a real operational cost most Laravel teams do not want, and it is why most teams end up on Deployer (deployer/deployer), Forge, Envoyer, Vapor, or a plain GitHub Actions + Docker pipeline instead..env and storage/ linking is mandatory. If you forget linked_files, you get Missing APP_KEY on the first request. If you forget linked_dirs, logs vanish on every release and session/login state appears to randomly reset.--force. migrate on production prompts for confirmation and, with no TTY, will fail or hang the deploy. Always php artisan migrate --force.storage/ but www-data reads it. Set a group-writable umask or an explicit chown/chmod hook in :finished, and test it before you need to roll back.cap -T lists every task, cap production deploy --dry-run shows the order without executing, and --trace prints the full backtrace and the exact shell command that failed. Read the failing command string before touching anything else — it is the fastest path to the fix.cap production deploy:rollback reverts the symlink but does not revert database migrations. If you need reversible schema changes, write down() methods and treat migrations as forward-only in practice.lib/capistrano/tasks/*.rake and load them from the Capfile.How can I help you explore Laravel packages today?