mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-12 06:48:55 +00:00
f446398dfd
Somebody followed the README's Docker quickstart, which starts the development stack, and got three failures in a row with nothing to search for (#1627): the worker died once a second on a missing autoloader, the site answered a bare 500, and once dependencies were installed by hand the setup screen threw ViteManifestNotFoundException. None of that is wrong behaviour for a clone — vendor/ and public/build/ are deliberately not in git — but every one of those failures kept its cause to itself. The preflight guard exists to turn "this was never set up" into a sentence, and it runs before the autoloader precisely so it can. It now answers two more questions: dependencies not installed, and frontend not built. The dependency check goes first, before the .env one, because the fix that branch prints — php artisan key:generate — cannot itself run without the autoloader, so reporting the key first hands somebody a second and more confusing error. A running vite dev server counts as built: public/hot means the assets come from there, and blocking a developer mid-session would be worse than the exception this replaces. The worker and scheduler exec straight into artisan, so before composer install they died instantly and restarted forever, filling the log that had to be read to fix it. They now print what is missing and exit slowly, and recover on their own once it is there. The scheduler gains the restart policy the worker already had — without one it exits during that window and stays exited, and scheduled work then silently never happens. Rehearsed on a genuine clone of the public repository, following the reporter's exact path: worker prints instructions instead of fatals (2 restarts in 30s, not 30), the browser gets "ProjectSend is not installed yet" naming composer install, then "not configured yet", then "not built yet" naming npm run build, then the setup screen. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
145 lines
4.3 KiB
YAML
145 lines
4.3 KiB
YAML
name: projectsend
|
|
|
|
services:
|
|
app:
|
|
build:
|
|
context: .
|
|
dockerfile: docker/app/Dockerfile
|
|
args:
|
|
WWWUSER: ${WWWUSER:-1000}
|
|
WWWGROUP: ${WWWGROUP:-1000}
|
|
volumes:
|
|
- .:/var/www/html
|
|
# Shared dev clones of the companion packages — never nested inside this
|
|
# repo. Relative to keep host and container paths symmetric with
|
|
# the ../packages symlink at this repo's own root.
|
|
- ../packages:/var/www/packages
|
|
environment:
|
|
PHP_IDE_CONFIG: serverName=projectsend
|
|
# Optional unattended first-admin creation; without these the web
|
|
# setup screen prompts on first visit.
|
|
ADMIN_NAME: ${ADMIN_NAME:-}
|
|
ADMIN_EMAIL: ${ADMIN_EMAIL:-}
|
|
ADMIN_PASSWORD: ${ADMIN_PASSWORD:-}
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_started
|
|
|
|
web:
|
|
build:
|
|
context: .
|
|
dockerfile: docker/web/Dockerfile
|
|
args:
|
|
WWWUSER: ${WWWUSER:-1000}
|
|
WWWGROUP: ${WWWGROUP:-1000}
|
|
ports:
|
|
- "${APP_PORT:-8090}:80"
|
|
volumes:
|
|
- .:/var/www/html
|
|
- ./docker/web/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
|
depends_on:
|
|
- app
|
|
|
|
worker:
|
|
build:
|
|
context: .
|
|
dockerfile: docker/app/Dockerfile
|
|
args:
|
|
WWWUSER: ${WWWUSER:-1000}
|
|
WWWGROUP: ${WWWGROUP:-1000}
|
|
command: php artisan queue:work --tries=3 --backoff=3
|
|
volumes:
|
|
- .:/var/www/html
|
|
- ../packages:/var/www/packages
|
|
# Required so `queue:restart` (triggered when mail provider settings
|
|
# are saved) actually brings the worker back instead of leaving the
|
|
# queue dead until someone runs `docker compose up -d` by hand.
|
|
restart: unless-stopped
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_started
|
|
|
|
scheduler:
|
|
build:
|
|
context: .
|
|
dockerfile: docker/app/Dockerfile
|
|
args:
|
|
WWWUSER: ${WWWUSER:-1000}
|
|
WWWGROUP: ${WWWGROUP:-1000}
|
|
command: php artisan schedule:work
|
|
volumes:
|
|
- .:/var/www/html
|
|
- ../packages:/var/www/packages
|
|
# Same reason the worker has one, plus a second: on a fresh clone this
|
|
# exits until `composer install` has run, and without a restart policy it
|
|
# then stays exited — scheduled work silently never happens, on the one
|
|
# setup where nobody would think to check.
|
|
restart: unless-stopped
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_started
|
|
|
|
db:
|
|
image: mysql:8.4
|
|
command: --mysql-native-password=OFF
|
|
environment:
|
|
MYSQL_DATABASE: ${DB_DATABASE:-projectsend}
|
|
MYSQL_USER: ${DB_USERNAME:-projectsend}
|
|
MYSQL_PASSWORD: ${DB_PASSWORD:-secret}
|
|
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root}
|
|
volumes:
|
|
- db-data:/var/lib/mysql
|
|
ports:
|
|
# Loopback only: this forward exists for host-side DB GUIs, not for
|
|
# the network. Without the prefix Docker publishes on 0.0.0.0 and
|
|
# bypasses most host firewalls — a LAN-reachable MySQL with the
|
|
# compose-file default password on any host that runs the stack.
|
|
- "127.0.0.1:${DB_PORT_FORWARD:-33061}:3306"
|
|
healthcheck:
|
|
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-p${DB_ROOT_PASSWORD:-root}"]
|
|
interval: 5s
|
|
timeout: 3s
|
|
retries: 10
|
|
|
|
redis:
|
|
image: redis:7-alpine
|
|
volumes:
|
|
- redis-data:/data
|
|
|
|
# Dev-only DB GUI (brief §12: ship Adminer as a dev-only Compose service)
|
|
adminer:
|
|
image: adminer:latest
|
|
ports:
|
|
# Loopback only — an unauthenticated DB panel must not be reachable
|
|
# from the network just because someone brought the dev profile up
|
|
# on a machine with a routable address.
|
|
- "127.0.0.1:${ADMINER_PORT:-8091}:8080"
|
|
environment:
|
|
ADMINER_DEFAULT_SERVER: db
|
|
depends_on:
|
|
- db
|
|
profiles:
|
|
- dev
|
|
|
|
# Dev-only SMTP catcher: lets email notifications be sent and inspected
|
|
# locally (web UI + HTTP API) without a real mail server.
|
|
mailpit:
|
|
image: axllent/mailpit:latest
|
|
ports:
|
|
# Loopback only, same reasoning as Adminer: captured mail is readable
|
|
# without authentication.
|
|
- "127.0.0.1:${MAILPIT_SMTP_PORT:-1025}:1025"
|
|
- "127.0.0.1:${MAILPIT_WEB_PORT:-8025}:8025"
|
|
profiles:
|
|
- dev
|
|
|
|
volumes:
|
|
db-data:
|
|
redis-data:
|