tightenco/takeout
Takeout is a Laravel package and CLI for running local development services like MySQL, Redis, Postgres, and Mailhog via Docker. Define the services you need in a simple config file, then start, stop, and connect to them with a single Artisan command.
Architecture fit. Takeout is a require-dev Composer package that generates and drives a per-project docker-compose.yml for backing services only — MySQL/MariaDB, PostgreSQL, Redis, Memcached, Mailhog/Mailpit, MinIO, and search. It is deliberately out of the application runtime: it does not ship Docker, does not rewrite .env, does not touch config/database.php, and does not manage PHP versions. That narrowness is the architectural selling point. It fits teams that want a thin, MIT-licensed, maintained wrapper around standard Docker tooling rather than an internal dev-harness build, and it fits per-project isolation as a first-class property: each app gets its own containers, ports, and named volumes.
Integration feasibility. Adoption is a single composer require --dev tighten/takeout, followed by ./vendor/bin/takeout list and ./vendor/bin/takeout enable <service>. The binary lives at vendor/bin/takeout, not Artisan. The only hard prerequisites are a running Docker engine and Compose v2 on PATH; both should be verified with docker compose version before installation, because Takeout shells out to docker compose and inherits every failure of that layer. Realistic pilot is half a day: enable MySQL, paste the printed port into .env, php artisan config:clear, php artisan migrate, then disable/enable and confirm data survived.
Technical risk.
enable assigns a free host port, may assign a different one after disable/enable, and never updates .env. The symptom is "connection refused," routinely misdiagnosed as credentials or auth-plugin failure./var/lib/postgresql/data → /var/lib/postgresql); anyone tracking postgres:latest was moved by an upstream push and hit initdb errors. Pin tags in the committed YAML immediately, for every service.docker-compose.yml and both bind host ports. Running both on one project is a known, confusing failure mode, not a preference.docker-compose.override.yml, which Compose merges automatically.platform: linux/amd64 in the override file for amd64-only images; Linux needs the user in the docker group; localhost resolves to ::1 on many setups, so 127.0.0.1 should be the documented default for DB_HOST and Redis.takeout disable preserves named volumes; docker compose down -v destroys them irrecoverably. Engineers routinely expect the opposite of whichever one they ran.Key questions for the decision record.
.env not committed, README documents the copy step) or determinism-first (fixed ports via --default-port or post-enable YAML edits, committed)?docker-compose.yml artifacts and the image-tag bump PRs?Stack fit. Laravel applications on composer, with Docker Desktop, OrbStack, or Colima on macOS and native Docker with docker group membership on Linux; Windows via Docker Desktop/WSL2. Host-run php artisan connects to containers over published ports on 127.0.0.1. Redis can serve cache, queue, and session from one container, so php artisan queue:work on the host works against it without an extra service. Mail is the highest-value, lowest-risk first adoption: set MAIL_MAILER=smtp and point at the printed Mailpit/Mailhog port to get a per-project web inbox.
Migration path.
.env, migrate, then commit the generated YAML.Compatibility notes.
latest.127.0.0.1, not localhost, for DB_HOST and Redis host.php artisan config:clear after every .env change; cached config is the second-most-common false alarm.platform: linux/amd64 in docker-compose.override.yml, never in the generated file, for amd64-only images on Apple Silicon..env values are perspective-dependent: 127.0.0.1:<published> for host-run PHP, service name and internal port if the app is later containerized. Do not copy .env across that boundary.Sequencing.
docker compose version; fix the Docker engine and Linux group membership before touching the package.composer require --dev tighten/takeout; run takeout list and takeout <command> --help to establish the authoritative service spelling..env; config:clear; migrate; run the disable/enable persistence check. This is the proof-of-concept gate.docker-compose.yml as a reviewable artifact.docker-compose.override.yml only when a real customization appears; never hand-edit the base file.takeout enable, and consider a composer setup script that stops by instructing the developer to run it. Do not automate port injection into .env — half-working automation here is worse than a two-line README.services: blocks with healthchecks; do not wrap Takeout into the pipeline.Maintenance. Low and mostly mechanical. Keep composer update in the normal cadence; the meaningful maintenance work is image-tag pinning, which converts upstream image pushes into reviewable PRs instead of surprise outages. Service changes go through takeout disable <service> && takeout enable <service>, not hand-editing, so the generated file stays reproducible. The committed docker-compose.yml and per-project config file are the durable artifacts and should live in the repo next to the code.
Support. Expect a predictable ticket queue, and put it in the README rather than rediscovering it per incident:
.env mismatch is ticket #1. The triage question is always "did you re-run enable, and does .env match docker compose ps?".env fix is #2 — php artisan config:clear.disable preserves volumes and down -v destroys them.docker group permissions round out the list; the latter looks like a Takeout bug and never is.docker compose ps, docker compose logs -f <service>, docker compose down -v, ./vendor/bin/takeout list.Scaling. Per-project containers and named volumes are the actual differentiator: an engineer can hold several client apps, branches, or demos live at once with isolated databases and no port collisions. Plan the ceiling explicitly — each MySQL/Postgres container is a few hundred MB of RAM — so engineers self-limit rather than discovering the limit during a demo. Org-wide, scaling is a standardization problem, not a technical one: one default orchestrator, one documented port stance, one README block, replicated per repository.
Failure modes to design around.
localhost resolving to ::1 against a container not listening on IPv6.initdb: directory not empty or a restart loop after an upstream move; the fix is regeneration plus a pinned tag, not hand-patching the mount.docker-compose.yml and host ports, producing genuinely confusing errors.down -v.enable.amd64-only images on Apple Silicon until a platform override is added.Ramp-up. A developer with working Docker is productive in well under a day; a team with a good README block is productive on their first clone. The high-leverage measurement is time-from-clone-to-first-successful-migrate for new hires, tracked before and after adoption, plus environment-variance incidents per onboarding. Assign one owner for the committed compose artifacts and tag bumps, and document the boundary of the tool explicitly — Takeout will not rewrite .env, manage PHP versions, provide an app runtime, or handle multi-repo networking. When someone wishes it did, that is the signal to evaluate Sail, DDEV, or a platform solution as a deliberate per-project choice, not to bolt capability onto Takeout.
How can I help you explore Laravel packages today?