marrow / anvil
A local Docker development environment for Marrow applications — Sail's role, Marrow's own name (forge shapes the code, Anvil runs it).
Requires
- php: >=8.2
- marrow/framework: ^2.2
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-10-01 13:42:00 UTC
README
Marrow Anvil
A local Docker Compose development environment for Marrow apps.
The same role Laravel Sail plays, under Marrow's
own name (forge shapes the code, Anvil is where you run it).
composer require --dev marrow/anvil php forge anvil:install
anvil:install is available immediately after composer require — no
manual registration step. The package declares its own module via
extra.marrow.modules in its composer.json, which Marrow's package
auto-discovery picks up automatically at boot.
What it creates
php forge anvil:install # app container only — matches the default SQLite driver php forge anvil:install --services=mysql,redis,node php forge anvil:install --services=pgsql,memcached,mailpit,minio php forge anvil:install --force # overwrite existing files
| File | Purpose |
|---|---|
docker-compose.yml |
The app service always, plus any requested --services |
docker/app/Dockerfile |
PHP 8.3-cli + the extensions Marrow needs (pdo_mysql, pdo_pgsql, pdo_sqlite, zip) + Composer |
anvil |
An executable CLI wrapper (chmod +x already applied on non-Windows) |
All three are plain generated files, safe to edit by hand afterward —
re-running anvil:install won't touch them again unless you pass --force.
The app service runs the same command as bare-metal dev
command: php forge serve --host=0.0.0.0 --port=8000
Nothing about how the app starts changes between Docker and non-Docker
development — only where it runs. There's no separate Nginx/PHP-FPM setup
to keep in sync with php forge serve's own behavior (colourised request
logging, crash auto-restart, bin/server.php routing) because it's the
literal same command.
The anvil CLI
./anvil up -d # start, detached ./anvil down # stop ./anvil forge migrate # -> docker compose exec app php forge migrate ./anvil composer require x # -> docker compose exec app composer require x ./anvil npm run build # -> docker compose exec node npm run build (needs --services=node) ./anvil shell # bash inside the app container ./anvil test # -> docker compose exec app composer test ./anvil ps / logs / build # passed straight to `docker compose`
Anything not in the list above falls through to docker compose directly,
so ./anvil <anything> always does something reasonable.
Available services
--services takes any comma-separated combination of:
| Service | Image | Notes |
|---|---|---|
mysql |
mysql:8.0 |
|
mariadb |
mariadb:11 |
MySQL-protocol compatible — same DB_DRIVER=mysql |
pgsql |
postgres:16-alpine |
DB_DRIVER=pgsql |
redis |
redis:7-alpine |
needs predis/predis |
memcached |
memcached:1.6-alpine |
no built-in Marrow cache driver yet |
mailpit |
axllent/mailpit |
SMTP catcher + web UI at :8025 |
minio |
minio/minio |
S3-compatible storage, console at :8900 |
node |
node:22-alpine |
runs npm run dev for Vite — see the framework's frontend docs |
Each database service you pick becomes an app depends_on. Running the
install command prints the exact .env keys to set for whatever you
chose — anvil:install never edits .env itself.
Example: --services=mysql
DB_DRIVER=mysql DB_HOST=mysql DB_DATABASE=app DB_USERNAME=marrow DB_PASSWORD=secret
(Same credentials as the generated docker-compose.yml's mysql
environment block — change both together if you change one.)
Known limitation: path-repository symlinks and volumes
If you're developing against a local path Composer repository that
points outside the project directory (common when working on Marrow
itself, or on a package, alongside the app that consumes it), vendor/
contains symlinks, not real files. docker-compose.yml only mounts
the project directory itself (.:/var/www/html), so a symlink pointing
outside it resolves to nothing inside the container — PHP fails to
require the autoloader.
This doesn't affect a normal install — a real composer require marrow/framework puts actual files under vendor/, nothing
to break. It only bites local path-repository development. Fix it with:
COMPOSER_MIRROR_PATH_REPOS=1 composer install
which forces Composer to copy path-repository packages into vendor/
instead of symlinking them.
Requirements
Docker and Docker Compose on the host. PHP is only needed there to run
php forge anvil:install itself — everything after that runs in the
container.
License
MIT — see LICENSE.
Made with ❤️ by Aure Dulvresse