savannabits/dockavel
Dockavel helps Laravel developers containerize their applications with ready-made Docker and Docker Compose scaffolding. Generate configuration for local development or deployment, streamline setup, and reduce manual Docker boilerplate in Laravel projects.
Dockavel scaffolds a Docker-based local development environment for a Laravel application — a Dockerfile, docker-compose.yml, and supporting service configs (nginx/web server, database, cache, queue worker) — so you can run the app without installing PHP, MySQL, or Redis on the host.
Minimal path:
composer require savannabits/dockavel --dev
php artisan vendor:publish --tag=dockavel-config # publishes config/dockavel.php
php artisan dockavel:install # scaffolds docker/ + compose files
docker compose up -d --build
Look at these first, in order:
config/dockavel.php — the package's central knob: PHP version, app/service names, ports, chosen database and cache drivers, and where files get published. Read this before running the install command so the first scaffold is close to correct.docker-compose.yml and docker/ directory — these are your files once published, not the package's. Treat them as application code.stubs/ directory (inside vendor/savannabits/dockavel) — this is what the install command copies, and the fastest way to understand what a config flag actually changes.First use case: a single Laravel app on a laptop with no local PHP/MySQL, brought up with docker compose up, serving on localhost:80 (or whatever port your config sets) and talking to a mysql service by container name.
If a prior docker-compose.yml from Sail or hand-rolled setup exists, remove or rename it before scaffolding to avoid two competing compose files.
Config-first, then scaffold. Because the install command is largely a copy of stubs, the workflow that ages best is: edit config/dockavel.php → run the install command → diff the output against what you had. Re-running with --force overwrites, so keeping the generated files in version control gives you a reviewable diff instead of a surprise.
Layer overrides rather than editing generated files. Keep docker-compose.yml as generated and add docker-compose.override.yml (picked up automatically) for personal tweaks — extra port mappings, Xdebug, a bind-mounted ~/.composer cache. Team members then get the same base with their own local layering, and regenerating the base stays cheap.
Split dev and CI. Use the same Dockerfile but different targets or build args for CI: a dev stage with Xdebug and writable volumes, and a leaner stage for test runs. Running php artisan test inside the app container (docker compose exec app php artisan test) keeps CI and local parity, which is the main reason to adopt a package like this at all.
Pin the service hostnames in .env. The generated environment expects DB_HOST=mysql (the compose service name), not 127.0.0.1. Set DB_HOST, REDIS_HOST, MAIL_HOST to service names and keep a .env.docker.example committed so new contributors don't debug connection refused on day one.
Queues and schedulers as separate services. Add queue and scheduler services running php artisan queue:work and php artisan schedule:work against the same image. This mirrors production topology and catches job bugs that never surface if you only run the web container.
Treat upgrades as a re-scaffold, not a composer update. v1.1.0 widened the supported Laravel range, which means the package can now be installed on newer Laravel versions — but the Docker assets already in your repo do not change. After bumping, re-run the install command into a scratch directory and diff, then merge deliberately.
Published files never update themselves. This is the single biggest trap. composer update savannabits/dockavel changes nothing in your running environment; the compose file and Dockerfile are frozen copies. Budget time for a manual diff on each release, especially around PHP version and base-image changes.
Laravel version support is a package constraint, not a Docker constraint. The v1.1.0 change (updated supported Laravel versions) only affects whether Composer will install the package. The PHP version in your Dockerfile is independent — check that the PHP image tag actually satisfies your app's composer.json require.php before rebuilding, or you'll get confusing failures during composer install inside the build.
Port collisions. 80/3306/6379 on the host will clash with a local MySQL, an existing Sail project, or a second checkout of the same repo. Run one project at a time, or parameterise ports in config and override per-worktree.
File ownership on Linux. Containers writing to bind-mounted volumes as root leave storage/ and bootstrap/cache/ unusable on the host. Set a UID/GID build arg matching your user, or add user: "${UID}:${GID}" in the override file. Related: bind-mounting the project root over the image's /var/www hides the vendor/ directory baked at build time — either keep vendor in a named volume or accept running composer install on the host too.
Windows and WSL2 bind-mount performance. Mounting from /mnt/c/... makes Laravel crawl. Keep the project inside the WSL2 filesystem and only reach for the Windows path when you must.
.env changes need a restart, not just a save. docker compose restart doesn't re-read environment files the way you'd expect if they're baked in via env_file at creation time — docker compose up -d to recreate.
Debugging order that actually works: docker compose ps (is it actually up?) → docker compose logs -f app (did PHP-FPM start?) → docker compose exec app sh then php -v and composer install -vvv (is the image sane?) → check nginx/web container logs for 502s, which almost always mean the app container is down or listening on the wrong port. docker compose config renders the merged compose file and is the fastest way to confirm an override actually applied.
Extension points are thin by design. There's no plugin system — customisation means editing published files. That's fine, but it means anything you hand-tune is invisible to future package versions, so document your deviations in the repo (a short docker/README.md) or the next person will re-scaffold over them.
How can I help you explore Laravel packages today?