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

Stacker Laravel Package

maxlab/stacker

MaxLab Stacker is a Laravel package for stacking multiple Eloquent models and treating them as a single model, simplifying bulk operations and model composition. It provides a clean, fluent API and integrates easily into existing PHP/Laravel projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Stacker is a Composer-installed CLI that wraps Docker Compose to give you a per-project local stack (nginx/apache, PHP-FPM, MySQL, Redis, Mailhog, Node) without hand-writing compose files. It is a local development tool — nothing here ships to production.

Minimal path:

# 1. Install globally (the 2017 release predates PHP 8 constraints)
composer global require maxlab/stacker --ignore-platform-reqs
export PATH="$HOME/.composer/vendor/bin:$PATH"   # or ~/.config/composer/vendor/bin

# 2. Sanity check
stacker --help          # confirm the verb set on your installed version
docker info             # Stacker is useless if the daemon isn't running

# 3. In an existing Laravel project root
cd ~/code/legacy-app
stacker init            # writes stacker.yml; answer the prompts
stacker start           # builds images, boots containers, registers the hostname

Where to look first, in order:

  1. stacker.yml in your project root — this is the whole contract. Project name, domain, PHP version, and which services exist. Everything else is derived from it.
  2. stacker --help / stacker <verb> --help — the CLI surface is small (start, stop, restart, build, ssh, exec, logs, plus teardown). Confirm exact names against your installed version rather than trusting blog posts.
  3. The generated compose file (usually under a .stacker/ or similar working directory) — when something is misbehaving, read what Stacker actually emitted instead of guessing.

Best first use case: a legacy Laravel 5.x app that needs PHP 7.1 and MySQL 5.7, running alongside your modern PHP 8 projects. That's Stacker's sweet spot — pinning old runtimes per project so your host machine stays clean. Don't start by converting your flagship app; start with the one you're afraid to composer update.

Then set the Laravel .env to match container networking:

APP_URL=http://legacy-app.test
DB_HOST=mysql          # service name, not 127.0.0.1
DB_PORT=3306
REDIS_HOST=redis
MAIL_HOST=mailhog
MAIL_PORT=1025

Implementation Patterns

Treat stacker.yml as the source of truth, and commit it. It's the cheap equivalent of a docker-compose.yml your whole team can read. Commit the file; keep machine-specific overrides (ports, absolute paths, Xdebug on/off) out of it or in a gitignored override.

Service selection should be deliberate, not maximal. A typical Laravel project needs php + nginx + mysql; add redis only if you use queues/cache, mailhog only if you send mail locally, node only if you can't run Node on the host. Every extra service is another container to debug and another port to collide. Start minimal, add on demand, and expect to stacker restart (or rebuild) after editing the service list — start alone often won't pick up a newly declared service.

Run tooling inside the container, not on the host. This is the whole point: your host PHP version becomes irrelevant.

stacker exec php composer install
stacker exec php php artisan migrate --seed
stacker exec php php artisan tinker
stacker ssh                      # interactive shell when you need to poke around

For long-running processes, keep them in a dedicated foreground terminal rather than backgrounding them inside the container:

# terminal 2 — queue worker
stacker exec php php artisan queue:work --tries=3
# terminal 3 — asset watcher (host-side, if Node is on the host)
npm run watch

If you use schedule:run, drive it from the host cron hitting stacker exec php php artisan schedule:run — do not try to install cron inside a container that gets recreated.

Isolate projects by domain, share by network. Give each project a unique hostname (app-one.test, app-two.test) and unique host ports. If two projects genuinely need to talk to each other (a Laravel API plus a separate admin SPA hitting its DB), put them on a shared Docker network and address services by container name — then DB_HOST=mysql becomes DB_HOST=app-one-mysql.

Database lifecycle deserves a script. Named volumes mean data survives restarts but dies on teardown. Make the dump/restore path explicit and boring:

# export before anything destructive
docker exec <mysql-container> mysqldump -u root -psecret app > dumps/app-$(date +%F).sql
# import a colleague's dump
stacker exec php php artisan db:seed   # or pipe the .sql through the mysql client

Wrap your team's five most common Stacker invocations in a Makefile (make up, make fresh, make ssh, make dump). New hires then never type a Docker command, and you have one place to swap the underlying tool later.

Xdebug should be off by default. Enable it only when you're actively stepping through code; leaving it on for every request makes the whole stack feel broken and is a perennial source of "why is this API slow" tickets. If your version exposes it as a stacker.yml toggle, flip it and rebuild; if not, use an env var on the php service.

Plan the exit ramp on day one. Keep .env values (DB_HOST=mysql, REDIS_HOST=redis, MAIL_HOST=mailhog) semantically aligned with what Laravel Sail or DDEV would use. If those names already match, migrating later is a compose-file swap plus a sail up, not an archaeology project.

Gotchas and Tips

The package is effectively unmaintained (last release March 2017, zero dependents). This is the headline risk, not a footnote. Practical consequences: PHP version constraints in composer.json will fight modern local PHP, Composer 2 resolution may complain, and no PHP 8 / Apple Silicon images exist. Mitigations: install it inside an isolated PHP 7.x runtime, use --ignore-platform-reqs, and accept that you may need to patch or vendor the package. Budget for this before you sell it to a team.

.local hostnames collide with mDNS/Avahi. On macOS especially, myapp.local can resolve to the Bonjour responder instead of your container, producing inexplicable timeouts. Prefer .test (or .localhost in newer browsers) for the domain in stacker.yml.

Hosts-file entries accumulate and are rarely cleaned up. Teardown doesn't always remove them. After a few months you'll have a dozen dead *.test entries and a confusing resolver. Audit /etc/hosts periodically; keep a note of what Stacker added.

Port collisions on 80/443/3306 are the #1 "it won't start" cause. Stop the host's nginx/Apache and system MySQL, or remap the host-side port in config and adjust APP_URL. When in doubt, docker ps and lsof -i :80 tell you immediately which it is.

File-sync performance will hurt on macOS and Windows. The 2017-era bind-mount approach predates :cached/mutagen and it shows on composer install and asset builds. Two rules: never sync node_modules into the container (host-side Node or a separate build step), and consider keeping vendor/ container-side if your sync is slow enough to make artisan feel laggy.

Permissions on storage/ and bootstrap/cache/ break constantly. Containers running as root create files your host user can't write to, and vice versa. When you see "failed to open stream: Permission denied," fix ownership inside the container (chown -R www-data:www-data storage bootstrap/cache) and check whether your version lets you set a UID.

Config changes usually require more than start. New services, changed PHP versions, and changed volume mappings generally need a rebuild/restart cycle — and stale images are a common reason a config edit appears to have no effect. If in doubt, stop, rebuild, start.

Teardown is destructive to database volumes. Whatever the teardown verb is called on your version, assume it wipes MySQL data. Dump first. Make this a reflex, and put it in the Makefile so it's automatic.

Debugging ladder when something's wrong:

  1. stacker logs <service> for the app/nginx/php logs.
  2. docker ps — is the container actually up, or restart-looping?
  3. Read the generated compose file to confirm your stacker.yml was interpreted as you expected.
  4. stacker ssh and test the connection from inside (e.g. mysql -h mysql -u root -p) to separate app config problems from networking problems.

Extension points to reach for before forking the package: a compose override file for extra services or volume tweaks, a per-project Dockerfile when you need a PHP extension Stacker doesn't ship, and .env-driven values injected into the stack. If you find yourself needing more than that, that's the signal to migrate to Laravel Sail or DDEV rather than maintain a 2017 wrapper — Stacker is a fine on-ramp for legacy projects, but it is not a foundation to build a 2020s workflow on.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package