# EZSCALE — Docker Compose Dev Environment Multi-service development stack for the EZSCALE Laravel app. Replaces the bare-metal `composer run dev` workflow with a fully-containerized environment that mirrors the production topology (nginx + PHP-FPM + MariaDB + Valkey + Horizon). ## Prerequisites - **Docker Engine 24+** with **Compose V2** (`docker compose`, not `docker-compose`) - **WSL2 with files cloned under `/home/$USER/`** (NOT `/mnt/c/...` — bind-mount performance is unusable on the Windows-mounted filesystem) - A modern browser (Chrome, Firefox, Safari) — `*.docker.localhost` resolution requires no `/etc/hosts` edits ## First-time setup ```bash make init ``` That single command: 1. Copies `.env.docker.example` → `.env.docker` 2. Builds all custom images (PHP-FPM, nginx, vite) 3. Pulls third-party images (Traefik, MariaDB, Valkey, Mailpit) 4. Boots MariaDB and Valkey, waits for them to be healthy 5. Runs `composer install` and `npm install` 6. Brings up the rest of the stack When `make init` finishes, you should be able to open: - **https://ezscale.docker.localhost** — marketing site - **https://account.ezscale.docker.localhost** — customer dashboard - **https://admin.ezscale.docker.localhost** — admin panel - **https://account.ezscale.docker.localhost/horizon** — Horizon dashboard - **https://mail.ezscale.docker.localhost** — Mailpit UI (catches all outgoing email) - **https://vite.ezscale.docker.localhost** — Vite dev server (for HMR) - **http://localhost:8080** — Traefik dashboard (insecure, dev-only) The first time you visit any of these, your browser will warn about a self-signed cert. Accept once — Traefik issues a wildcard cert covering all subdomains. ## Daily commands ```bash make up # bring stack up make down # stop (volumes preserved) make logs # tail all logs make logs SVC=horizon # tail one service make sh # bash inside app container make artisan ARGS="migrate" # any artisan command make composer ARGS="require foo/bar" make npm ARGS="install foo" make test # php artisan test --compact make pint # format dirty PHP make fresh # migrate:fresh --seed make destroy # nuclear: stop + wipe volumes ``` `make help` prints the full list. ## Architecture | Service | Image | Role | |---------|-------|------| | `traefik` | `traefik:v3.6` | Edge proxy + TLS termination + routing | | `app` | custom (PHP 8.3-FPM Debian) | Application container | | `web` | `nginx:1.30-alpine` | Serves `public/`, proxies PHP | | `vite` | custom (Node 24 Alpine) | HMR dev server | | `horizon` | same as `app` | Queue worker supervisor | | `scheduler` | same as `app` | `schedule:work` runner | | `mariadb` | `mariadb:12` | Primary database | | `valkey` | `valkey/valkey:9-alpine` | Sessions/cache/queues | | `mailpit` | `axllent/mailpit:v1.29` | SMTP catcher | Traefik routes the three Laravel subdomains (marketing/account/admin) to the same nginx container. Laravel's `Route::domain()` in `bootstrap/app.php` handles per-subdomain dispatch internally. ## Environment The stack reads `.env.docker` at the repo root — separate from `website/.env`. This keeps the Docker workflow from fighting any bare-metal `composer run dev` setup you might still want to use. Critical Docker-specific values: ``` DB_HOST=mariadb REDIS_HOST=valkey MAIL_HOST=mailpit APP_URL=https://ezscale.docker.localhost ``` Third-party API keys (Stripe, PayPal, VirtFusion, etc.) need to be added to your `.env.docker` if you want to test those integrations. ## Volumes Persisted across `make down` (lost only on `make destroy`): - `mariadb_data` — MySQL data - `valkey_data` — Valkey AOF persistence - `mailpit_data` — captured email - `traefik_certs` — self-signed cert cache The Laravel source (`./website`) is bind-mounted live — your edits show up immediately. `vendor/` and `node_modules/` are visible on the host for IDE autocomplete. ## TLS Traefik auto-generates a single self-signed wildcard cert for `*.ezscale.docker.localhost` on first boot. The cert lives in the `traefik_certs` volume. If you want a green-padlock experience instead of accepting the warning once per browser: ```bash brew install mkcert # or apt/winget equivalent mkcert -install mkcert -cert-file docker/traefik/certs/cert.pem \ -key-file docker/traefik/certs/key.pem \ "ezscale.docker.localhost" "*.ezscale.docker.localhost" ``` Then update `docker/traefik/dynamic.yml` to point `certificates:` at those files. Currently configured for self-signed; mkcert is left as a future enhancement. ## Common gotchas **Port 80 or 443 already in use.** Edit `docker-compose.yml`'s `traefik` service and remap to e.g. `8000:80`, `8443:443`. Then access the stack at `https://ezscale.docker.localhost:8443`. **`make init` hangs on "waiting for mariadb".** First MariaDB boot creates the system tablespace and can take 20-30s. The healthcheck has a 30s `start_period` to accommodate this. If it really stalls, `make logs SVC=mariadb` to see why. **Permission errors on `storage/logs/laravel.log`.** The PHP container's UID matches your host UID (1000) by default. If your host UID differs, rebuild with `UID=$(id -u) GID=$(id -g) make build`. **Horizon dashboard 403s.** Horizon's gate is in `App\Providers\HorizonServiceProvider::gate()`. In dev all admin users have access; you need to log in with an admin role first. **Vite assets don't load.** Check `make logs SVC=vite`. The Laravel Vite plugin auto-injects the dev URL — if it can't reach `https://vite.ezscale.docker.localhost`, assets fall back to the manifest. Make sure that hostname is reachable in your browser. **Composer/npm install slow.** First-time `composer install` takes 1-2 min. After that the `vendor/` dir is cached on disk. Same for `node_modules`. ## Co-existing with bare-metal dev This stack does NOT delete or modify `website/.env`. If you previously used `cd website && composer run dev`, that still works — it reads `website/.env` and connects to whatever local PHP/MySQL/Redis you had. Pick one or the other for any given session. Don't run both simultaneously (they'd fight over ports and sessions). ## Spec Full design rationale in [`docs/superpowers/specs/2026-04-25-docker-compose-dev-environment-design.md`](../docs/superpowers/specs/2026-04-25-docker-compose-dev-environment-design.md).