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

Deployer Laravel Package

rebelinblue/deployer

Rebelinblue Deployer is a Laravel package for simple, zero-downtime deployments. Configure servers and release steps, publish atomic releases, and roll back safely using Artisan commands—without complex deployment tooling.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: it's an application, not a package. rebelinblue/deployer is a self-hosted Laravel app (an Envoyer-style deployment manager), not something you composer require into an existing project. You run it as its own service, point it at your repos, and it SSHes into your servers to deploy them. That reframing determines everything below.

Minimal setup:

  1. Clone the repo into its own vhost/subdomain (deploy.example.com), composer install, copy .env.example → .env, php artisan key:generate.
  2. Provision MySQL, and either Redis or the database queue driver. Check composer.json for the PHP constraint before you start — historically this has been a Laravel 5.x-era app. Don't assume from old notes: 0.0.47 is still shipping fixes, so read the actual constraint and test booting on your target PHP.
  3. php artisan migrate, create the storage symlink, build assets (the Node toolchain is old — expect to fight node-sass/webpack if you rebuild assets; many people just serve the committed build).
  4. Run a queue worker under Supervisor and the scheduler via cron. Deployments are queued jobs; without a worker, deploys sit in "pending" forever and you'll think it's broken.
  5. Register the first admin user, then create a Project (repo URL + branch), an Environment for it, and a Server with SSH credentials. Verify connectivity with whatever "test connection" affordance the server screen offers before configuring steps.
  6. If upgrading an instance that already manages live projects: back up every target server's shared folder before the next deploy. 0.0.47 recreates the shared folder structure on first deploy of an existing project, backs up the old one, and attempts to migrate files. Take your own backup, then diff the new structure against the old after that first deploy.

Where to look in the codebase when you need to change behaviour:

  • routes/ — the webhook endpoints and the project/environment CRUD are the two things you'll touch most.
  • app/Jobs/ (or equivalent) — the job that actually runs a deployment; this is the heart of the app.
  • app/Http/Controllers/ — project, environment, deployment, and heartbeat controllers.
  • config/ — notification channels, queue, and any app-specific settings.
  • database/migrations/ — the schema tells you the real domain model faster than the UI does.

First use case: get one environment deploying manually from the UI before you wire up webhooks. A manual deploy with three steps (git clone/checkout, composer install --no-dev -o, symlink switch) proves SSH, paths, and permissions all work. Only then add the webhook.

Implementation Patterns

The domain model is the workflow. Project → Environment → Server(s) → Deployment → Steps. A deployment is an ordered list of shell commands executed over SSH against the target server(s). Internalise this and the UI stops feeling arbitrary: everything you configure is either "where do I run it" (servers/environment) or "what do I run" (steps).

Use the releases + current symlink pattern. The idiomatic setup (borrowed from Capistrano) is: deploy into releases/<timestamp>, then atomically repoint a current symlink. Do it this way even though the tool allows you to deploy in place — it gives you near-instant rollback (repoint the symlink to the previous release) and avoids half-broken docroots. Structure your steps as:

  1. Create release directory / clone or fetch the branch at the target commit.
  2. Install dependencies (composer install --no-dev --optimize-autoloader).
  3. Run migrations (php artisan migrate --force).
  4. Warm caches (config:cache, route:cache, view:cache) — in that order, after env vars are in place.
  5. Repoint the current symlink.
  6. php artisan queue:restart so long-running workers pick up new code.

Lean on shared folders for anything that must survive a deploy. .env, storage/, uploads, and logs belong in the shared area, symlinked into each release. This is exactly the feature 0.0.47 tightened up: symlinks are now created with full paths, which removes a whole class of "worked from the app, broke under cron/fpm" relative-path bugs. Keep your shared-folder list explicit and small, and audit it during the upgrade since the structure is being rebuilt once.

Treat every step as idempotent and non-interactive. A step that prompts (Composer asking for a token, an ssh host-key confirmation, npm asking to install) will hang the deployment until it times out. Set COMPOSER_AUTH/COMPOSER_HOME, pre-seed known_hosts, use -n/--no-interaction everywhere. Because a failed step leaves earlier steps applied with no automatic rollback, steps must be safe to re-run.

Put secrets in environment variables, not in step commands. The step text is stored in the database and rendered in the UI — anyone with dashboard access sees it. Keep DB passwords, API keys, and .env contents in the environment's variable configuration, and let steps reference the generated file. Generate the app's .env from those variables as an early step, ideally writing it into the shared folder so it persists across releases.

Prefer absolute, deploy-owned paths; tildes now work but stay explicit. 0.0.47 fixed ~ expansion, so ~/app/current no longer misbehaves. Still write /home/deploy/app/... in steps: absolute paths are unambiguous in logs, survive sudo steps (where ~ may resolve to root), and match the new full-path symlink behaviour. If you previously worked around the tilde bug with hardcoded /home/<user> strings, that workaround is now cruft — clean it up.

Wire webhooks per project, and filter by branch. GitHub/GitLab/Bitbucket payload URLs plus a secret are the intended automation path. Always set the secret — an unauthenticated deploy endpoint is remote code execution by proxy. Confirm the branch filter actually matches your branch naming (main vs master catches everyone once), and remember the Deployer host must be able to reach the payload and (for clone-from-origin setups) the repo.

Fan out notifications at the environment level. Slack/email/webhook channels per project or environment mean the team sees failures where they already are. Treat a failing deploy notification as a paging signal — it usually precedes a real outage if your symlink switch is late in the step list. Since 0.0.47 logs stderr from processes that exited non-zero, the notification now links to an actually useful log rather than a silent failure.

Use heartbeats for cron. The heartbeat feature is an underrated part of this app: cron jobs curl a URL, and the dashboard alerts you when a job stops reporting. Cheap, effective uptime monitoring for scheduled work — set it up alongside your deployments.

Run it as a dedicated deploy user. A non-root deploy account with a passphrase-less key and narrowly scoped sudo for reload commands (php-fpm, nginx) keeps the blast radius small and makes the SSH permission model obvious when something fails.

Gotchas and Tips

  • Upgrading to 0.0.47 rebuilds the shared folder on first deploy of an existing project. Deployer backs up the old folder and tries to migrate files, but this is a one-shot, best-effort operation on production servers. Take your own backup, deploy one low-risk environment first, and confirm .env, storage/, uploads, and logs all landed in the new structure before rolling it across everything.
  • Re-check the maintenance/PHP story rather than trusting stale notes. Older write-ups call this app archived and PHP 7-only; 0.0.47 is still landing fixes (including a security-adjacent one for SSL certificate passphrases). Read composer.json, pin your PHP version in the vhost, commit composer.lock, and keep the dashboard behind auth. If you're starting fresh, still compare against deployer/deployer, Envoyer, Ploi, or plain CI/CD — adopting this means you own it.
  • SSL certificates with a passphrase now work — audit existing ones. If you previously stored a passphrase-protected cert and worked around the bug (stripping the passphrase, or uploading a bundled key), re-save the cert through the UI and confirm renewal/reload steps still succeed. The passphrase lives in the app's database, so treat DB exports and backups as secrets.
  • "Pending forever" = no queue worker. The most common first-day bug. Verify queue:work is running and check failed_jobs before debugging anything else.
  • Deployment output is your debugger — and it just got better. Each deployment stores per-step stdout/stderr, and 0.0.47 adds error output for processes that didn't exit cleanly, so a step that used to fail silently now has a real message. Read the failing step's raw output first (permissions, missing binary, wrong path) before touching code.
  • Relative paths in scripts are now the odd one out. With full-path symlinks for shared folders, code that cds into current and dereferences storage works, but hand-written scripts assuming relative symlink targets may not. Grep your step list and any custom scripts for relative symlink assumptions.
  • Symlink and permissions ordering bites. storage:link and any writable directories (storage/, bootstrap/cache/) need the correct owner for the web user, not the deploy user. Do this as an explicit step against the shared folder, not the release, or you'll get intermittent 500s after an otherwise "successful" deploy.
  • config:cache before env changes is a classic footgun. Caching config then mutating .env means the app silently uses stale values. Order steps: write env → clear → cache.
  • Rollback isn't magic. If your steps aren't release-directory-based, "rollback" is re-running an old deployment. With the symlink pattern it's a one-liner; without it, it's a rebuild.
  • SSH is the failure surface. Host key mismatches, key permissions (chmod 600), wrong deploy path, non-interactive hangs. Keep a manual ssh deploy@host 'echo ok' as your first diagnostic.
  • Webhook secret + branch filter. Two independent things people forget; both cause "it didn't deploy on push."
  • Scheduler must be running for heartbeats and any cleanup jobs — a silently dead scheduler means you're flying blind on cron monitoring.
  • Extension points are plain Laravel. New notification channels, extra deployment step types, or custom reporting are best done as small additions to the existing controllers/jobs and a migration — fork it, don't patch vendor code. Keep your fork's diff small and tracked, and pull upstream releases like 0.0.47 promptly (bug fixes plus an SSL credential fix are not things you want to re-implement yourself), because you're the maintainer of your own deployment system either way.
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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console