# Copy to run/.env and adjust as needed.
# docker compose reads this file automatically, and the run scripts source it
# with bash — so quote any value containing spaces or shell characters.
# ---- Project ----------------------------------------------------------------
# Used for the compose project name, the local image names, the dashboard title
# and the macOS helper agent label. Keep it lowercase and unique per stack.
COMPOSE_PROJECT_NAME=myapp
PROJECT_LABEL="My App"
# ---- Repository layout ------------------------------------------------------
# Paths to the two repositories this stack runs, relative to run/ (absolute
# paths also work). The default layout is:
# workspace/
# backend/ Laravel app
# frontend/ pnpm monorepo (apps/* + packages/*)
# run/ this repository
BACKEND_DIR=../backend
FRONTEND_DIR=../frontend
# ---- Backend stack ----------------------------------------------------------
# laravel -> docker/backend/laravel (php + composer + artisan)
# node -> docker/backend/node (node + pnpm/npm/yarn, commands below)
BACKEND_STACK=laravel
# Set when the API is a subdirectory of a larger repository (a monorepo holding
# both the API and the frontend). Point BACKEND_DIR at the repository root and
# BACKEND_SUBDIR at the app, e.g. BACKEND_DIR=../mono BACKEND_SUBDIR=apps/api.
# The whole repo is mounted so workspace symlinks resolve; commands run in the
# subdirectory.
BACKEND_SUBDIR=
# Defaults to the app directory's .env — the file the dashboard reads seeded
# credentials from.
BACKEND_ENV_FILE=
# Node backends only. Each is run with bash -lc inside the container, from the
# repository root. Leave one empty to skip that step.
# BACKEND_INSTALL_CMD defaults to the lockfile's package manager;
# "none" skips it (monorepo: frontend-deps installs)
# BACKEND_BUILD_CMD e.g. "pnpm build" (usually empty in dev)
# BACKEND_START_CMD defaults to "npm run dev" — must listen on $PORT
# BACKEND_MIGRATE_CMD e.g. "npx prisma migrate deploy"
# BACKEND_SEED_CMD e.g. "npx prisma db seed" (runs once, see run/scripts)
# BACKEND_FRESH_CMD e.g. "npx prisma migrate reset --force"
# BACKEND_QUEUE_CMD e.g. "pnpm worker"
# BACKEND_SCHEDULE_CMD e.g. "pnpm cron"
BACKEND_INSTALL_CMD=
BACKEND_BUILD_CMD=
BACKEND_START_CMD=
BACKEND_MIGRATE_CMD=
BACKEND_SEED_CMD=
BACKEND_FRESH_CMD=
BACKEND_QUEUE_CMD=
BACKEND_SCHEDULE_CMD=
# Workers. Turn off for a backend that has none.
RUN_QUEUE=true
RUN_SCHEDULER=true
# ---- Frontend apps ----------------------------------------------------------
# Workspace names of the Vite apps (as in each apps/<dir>/package.json "name").
WEB_APP=web
ADMIN_APP=admin
LANDING_APP=landing
# Extra workspace packages to install and build with the web apps, without
# serving them — e.g. "api" when a Node backend lives in this same monorepo.
EXTRA_DEPS_APPS=
# Other apps in the workspace to serve, beyond web/admin/landing — the apps a
# monorepo grows after the first init. Workspace names, space separated. Each
# one is run exactly like the apps above, with a port and (optionally) a start
# command named after it:
#
# EXTRA_APPS="reports partner-portal"
# REPORTS_PORT=5180
# REPORTS_CMD=
#
# Add one with: run-stack init --update
EXTRA_APPS=
# Workspace package watching. frontend-packages rebuilds every imported
# package's dist/ on source edits, then restarts the app dev servers so they
# serve the fresh output (a Vite server's watch root never covers packages/).
# frontend-sync reruns the install when a package.json or the lockfile changes
# on the host, which the node_modules volumes cannot receive any other way.
# All values are seconds.
DEPS_SYNC_INTERVAL=5
DIST_RESTART_INTERVAL=2
DIST_SETTLE_SECONDS=3
# Metro cannot be told about host edits: it watches with fs.watch, and inotify
# events do not cross a Docker Desktop bind mount, so a change on the host is
# invisible until the cache is cleared. A write made inside the container does
# raise inotify, so the Metro container re-touches whatever the host changed —
# no content is written, Metro re-reads the files itself.
#
# METRO_POKE=false turns it off (for a stack whose Metro runs on the host).
# The interval is how often it looks; the settle is how long it waits for a
# burst of saves to finish, so one edit costs one rebuild.
METRO_POKE=true
METRO_POKE_INTERVAL=1
METRO_POKE_SETTLE=1
# How each app is started, when its own script is not what this stack assumes
# (a bare `vite` for the web apps, `expo start` for mobile). Apps disagree here:
# a Vite app usually has "dev" while an Expo app has "start". ./run.sh init
# fills these in from each app's package.json.
# The port and host flags are appended by the container, so leave them out:
# WEB_CMD="pnpm --filter @acme/storefront run dev"
# MOBILE_CMD="pnpm --filter @acme/mobile-app run start"
WEB_CMD=
ADMIN_CMD=
LANDING_CMD=
DESKTOP_CMD=
MOBILE_CMD=
# Turn individual apps off on machines that do not need them.
RUN_ADMIN=true
RUN_LANDING=true
# ---- Desktop ----------------------------------------------------------------
# The renderer (a Vite app in the same monorepo) runs in Docker; the Electron /
# Tauri shell needs a GUI, so ./run.sh desktop launches it on the host.
# electron | tauri | none
DESKTOP_STACK=none
RUN_DESKTOP=false
# Workspace name of the desktop app.
DESKTOP_APP=desktop
# Run on the host from FRONTEND_DIR. Empty falls back to a per-stack default:
# electron -> pnpm --filter <app> exec electron .
# tauri -> pnpm --filter <app> exec tauri dev
DESKTOP_HOST_CMD=
# ---- Infrastructure ---------------------------------------------------------
# postgres | mysql | none
DB_ENGINE=postgres
RUN_REDIS=true
RUN_MAILPIT=true
# S3-compatible object storage (MinIO).
RUN_MINIO=false
MINIO_ROOT_USER=minio
MINIO_ROOT_PASSWORD=minio123
MINIO_BUCKET=local
# ---- Host ports -------------------------------------------------------------
BACKEND_PORT=8000
# The port the API listens on inside its container. Leave it at 8000 for an app
# that honours $PORT; set it to the hardcoded number for one that does not, so
# BACKEND_PORT still reaches the server.
BACKEND_CONTAINER_PORT=8000
WEB_PORT=5173
ADMIN_PORT=5174
LANDING_PORT=5175
DESKTOP_PORT=5176
MOBILE_CLIENT_PORT=8081
POSTGRES_PORT=5434
MYSQL_PORT=3307
REDIS_PORT=6380
MAILPIT_UI_PORT=8025
MINIO_PORT=9000
MINIO_CONSOLE_PORT=9001
DASHBOARD_PORT=8090
HOST_OPEN_PORT=8091
# ---- Docker resource limits -------------------------------------------------
# Optional. When set, scripts/gen-resources.sh writes docker-compose.resources.yml
# (Compose cannot interpolate empty cpus/mem_limit, so unset means unlimited).
# Memory limit (e.g., 512m, 2g, 4g)
# DOCKER_MEMORY_LIMIT=2g
# CPU limit (number of cores, e.g., 1, 2, 4)
# DOCKER_CPU_LIMIT=2
# Shared memory size (e.g., 64m, 128m, 256m)
# DOCKER_SHM_SIZE=256m
# Per-service resource limits (override global limits for specific services)
# BACKEND_MEMORY_LIMIT=4g
# BACKEND_CPU_LIMIT=4
# FRONTEND_MEMORY_LIMIT=2g
# FRONTEND_CPU_LIMIT=2
# POSTGRES_MEMORY_LIMIT=2g
# POSTGRES_CPU_LIMIT=2
# MYSQL_MEMORY_LIMIT=2g
# MYSQL_CPU_LIMIT=2
# ---- Sibling stacks ---------------------------------------------------------
# Other compose projects to show on this dashboard, so one board covers every
# stack you run locally. Format: id|Label|host port|health path (repeatable,
# separated by ";"). Quote it — the run scripts source this file with bash.
# Example: EXTRA_SERVICES="other/api|Other API|4000|/health"
EXTRA_SERVICES=
# Compose project names the dashboard reads container state for. Leave unset to
# use COMPOSE_PROJECT_NAME. Comma-separated real names only — do not write
# ${COMPOSE_PROJECT_NAME} here; dotenv will not expand it.
# COMPOSE_PROJECTS=myapp,other
# ---- Database ---------------------------------------------------------------
# Engine is DB_ENGINE above; these apply to whichever one runs.
DB_DATABASE=myapp
DB_USERNAME=myapp
DB_PASSWORD=secret
DB_ROOT_PASSWORD=secret
# ---- Laravel (BACKEND_STACK=laravel) ---------------------------------------
# Leave APP_KEY empty to reuse the key already in the backend's .env
APP_KEY=
APP_ENV=local
APP_DEBUG=true
# Run `php artisan migrate --force` on backend startup
RUN_MIGRATIONS=true
# Also run `php artisan db:seed --force` on startup (only when migrations run)
RUN_SEEDERS=true
# Endpoint the dashboard probes to decide the API is up, and the login endpoint
# its credentials panel posts to.
BACKEND_HEALTH_PATH=/api/health
BACKEND_LOGIN_PATH=/api/auth/login
# ---- Frontend ---------------------------------------------------------------
# Base URL the browser uses to reach the API (must be host-reachable, not the
# compose service name).
VITE_API_BASE_URL=http://localhost:8000/api
VITE_WEB_APP_URL=http://localhost:5173
VITE_SENTRY_DSN=
VITE_SENTRY_ENV=local
# ---- Mobile (Metro) ---------------------------------------------------------
# Metro runs in Docker. The iOS Simulator / Expo Go still run on the Mac.
# Set to false on machines that do not develop mobile.
RUN_MOBILE=true
MOBILE_APP=mobile-client
# Workspace packages the mobile watcher rebuilds (space separated). Empty means
# every package the mobile app depends on.
MOBILE_WATCH_PACKAGES=
# Native ids, used to relaunch an already installed build instead of rebuilding.
IOS_BUNDLE_ID=
ANDROID_PACKAGE=
# Leave empty (or localhost) and ./run.sh fills in this machine's LAN IP so
# Simulator and Expo Go on a phone can both reach Metro in Docker.
REACT_NATIVE_PACKAGER_HOSTNAME=
EXPO_PUBLIC_API_BASE_URL=
# ---- Deploy -----------------------------------------------------------------
# ./run.sh deploy <web|mobile|desktop> [env], and the Deploy button on those
# dashboard cards. Each target picks a preset command; DEPLOY_*_CMD overrides it
# with anything you like. $DEPLOY_ENV holds the environment argument (default
# DEPLOY_DEFAULT_ENV), $DEPLOY_APP the workspace name.
#
# Presets:
# web vercel | netlify | docker | ssh | none
# mobile eas | none
# desktop electron-builder | tauri | none
DEPLOY_DEFAULT_ENV=staging
DEPLOY_ENVS="staging production"
DEPLOY_WEB_TARGET=none
DEPLOY_WEB_CMD=
# docker preset: image is built from the app and pushed here.
DEPLOY_WEB_IMAGE=
# ssh preset: dist/ is rsynced to this destination.
DEPLOY_WEB_SSH_DEST=
# eas preset runs: eas build --platform $DEPLOY_MOBILE_PLATFORM --profile $DEPLOY_ENV
DEPLOY_MOBILE_TARGET=none
DEPLOY_MOBILE_CMD=
DEPLOY_MOBILE_PLATFORM=all
# electron-builder / tauri presets write installers under DEPLOY_DESKTOP_OUT.
DEPLOY_DESKTOP_TARGET=none
DEPLOY_DESKTOP_CMD=
DEPLOY_DESKTOP_OUT=dist