shipping-docker/vessel
Vessel provides a lightweight, Docker-based local development environment for Laravel applications. It replaces Homestead/Vagrant with simple containers for PHP, MySQL, Redis, and more, making setup fast and reproducible across teams.
Vessel is a Docker-based local development environment for Laravel — it ships a docker-compose.yml, per-service Dockerfiles, and a vessel CLI wrapper. You are not adding a runtime dependency to your app; you are adding a reproducible dev stack.
Minimal steps:
composer require shipping-docker/vessel --dev
php vendor/bin/vessel install # publishes docker-compose.yml, docker/, .env.docker bits
./vessel up # or: docker-compose up -d
Then browse http://localhost. The generated docker-compose.yml exposes the app on :80, MySQL on :3306, and Redis on :6379.
Where to look first:
docker-compose.yml at the repo root — the source of truth for services, ports, and volumes.docker/ — one directory per service (app, mysql, redis, etc.). The app Dockerfile is where PHP version, extensions, and Composer live.vessel script — a thin wrapper; read it once so you know exactly which docker-compose exec each command maps to..env — DB_HOST=mysql, REDIS_HOST=redis, DB_PORT=3306 (the container port, not the host-mapped one).First real use case: get vessel artisan migrate and vessel composer install working, then point your IDE/DB client at 127.0.0.1:3306. That's the whole onboarding loop.
Upgrading to 6.0.0 specifically: after composer update, re-run the vendor install/upgrade step from the Vessel docs, then rebuild the app image — step 4 ("Rebuild Containers") of the upgrade guide — before vessel up. Skipping the rebuild leaves you on the old Ubuntu base and produces confusing "file exists / package not found" errors.
Daily command wrapping. Treat vessel as the only entry point so nobody on the team shells into containers by hand:
vessel artisan make:model Invoice -m
vessel composer require laravel/horizon
vessel npm run dev
vessel mysql # drops you into the MySQL client as the app user
vessel bash # ad-hoc shell in the app container
vessel logs -f app
Alias it in your shell (alias v=vessel) — it removes the constant "do I need docker-compose exec app or docker-compose run?" friction.
Override, don't fork. Put project-specific tweaks in docker-compose.override.yml (Compose merges it automatically) rather than editing docker-compose.yml. Typical overrides: extra port mappings, a Mailhog/Mailpit service, mounting ~/.composer for cache reuse, or a xdebug env toggle. This keeps the published files upgradable across Vessel majors — which matters a lot at 6.0.0, where re-publishing would otherwise clobber your edits.
Extend the app image instead of editing it. If you need an extension, a Node version bump, or a custom entrypoint, add a small docker/app/Dockerfile layer or a build-arg toggled from .env. Vessel's app Dockerfile is deliberately plain Ubuntu + PHP; your additions survive base-image bumps better when they're additive.
Multi-service workflows. Queue workers and schedulers are the usual second/third container. Either add them as compose services sharing the same image and volume, or run them on demand (vessel artisan queue:work). For Horizon/Telescope, keep the worker service's command in docker-compose.override.yml so local and CI diverge cleanly.
CI parity. Because the stack is plain Compose, CI can reuse the same docker-compose.yml with docker-compose build app and run tests via docker-compose run --rm app vendor/bin/phpunit. Pin the base image tag and the Vessel composer constraint so a composer update on a laptop doesn't silently change the CI PHP version.
Swagger/static content. 6.0.0 lets PHP (via the built-in server) serve static content without an HTTP 303 redirect. If you serve generated API docs (L5-Swagger, api-docs.json, Swagger UI assets) from public/, verify they're still reachable through the app container after upgrading — that 303 was previously masking asset-resolution issues for some setups.
Predis handling. 6.0.0 stops installing Predis during init. If your app or a package relies on the predis client rather than the phpredis extension, add it explicitly (vessel composer require predis/predis) or switch REDIS_CLIENT to phpredis and ensure the extension is built into your app image. Decide this deliberately per project — it's now a project concern, not a Vessel default.
docker-compose build --no-cache app && vessel up. Layer caching is the #1 cause of "I upgraded but nothing changed." Ubuntu 20.04 brings newer PHP packages, so expect extension names/versions to shift; pin php versions in the Dockerfile if you need determinism.DB_HOST=mysql, never 127.0.0.1. Inside the network, localhost is the app container. This is the most common first-run failure, and it surfaces as a SQLSTATE[HY000] [2002] connection refused.3306, 80, 6379 clash with a local MySQL/nginx/Redis or with a second Vessel project. Override the host side in docker-compose.override.yml ("3307:3306"), leaving the container port alone.storage/ and bootstrap/cache/ often end up root-owned after container writes. Set UID/GID build args or run vessel bash as the matching user; otherwise artisan mysteriously fails to write logs.vendor/ on a bind mount is slow. Prefer named volumes for vendor//node_modules/ or enable the Docker file-sharing tuning; don't conclude Vessel is slow before checking this..env drift between host and container. Vessel's install writes container-appropriate values; if you later copy production .env down, you'll re-break DB/Redis hosts. Keep a .env.docker or document the three variables that must differ.vessel vs docker-compose mismatch. If a teammate runs docker-compose up with an older Vessel, Compose file version differences bite. Pin the package version in composer.json (e.g. "shipping-docker/vessel": "^6.0") and commit the lockfile..env copies — review git status after vessel install and after upgrading, and keep docker/ diffs small and intentional so the next major bump is a merge, not a rewrite.9003 for Xdebug 3) — a silent, always-off debugger wastes hours.vessel ps → vessel logs -f <service> → vessel bash into app → docker-compose config to verify merged YAML. Most "Vessel is broken" reports resolve at step 4 (the override file isn't merging what you think it is).How can I help you explore Laravel packages today?