Files
projectsend/compose.yaml
T
ignacionelson 92a132d74f Give zip builds their own queue, so one archive cannot hold up the mail
The last piece of the #1687 follow-up. BuildZipDownloadJob allows itself
an hour, every shipped topology runs exactly one worker, and everything
shares the default queue -- so one large archive delayed every
notification email queued behind it. The size cap and the
one-build-per-person rule bounded that in July; they did not remove it.

onQueue('zips') in the constructor rather than at the dispatch site, so a
second caller cannot forget it. Both images grow a worker for it:
compose.yaml gains worker-zips, supervisord gains [program:queue-zips],
and the existing worker in each narrows to --queue=default. --tries=1
there matches the job, which records its own failure rather than being
retried.

The part that needs care is the manual install. A worker whose command
still says plain `queue:work` consumes `default` only, so it would send
email happily and never finish a single zip, with nothing in any log
saying why. INSTALL.md's unit now reads --queue=default,zips -- one
worker watching both, which is right for most installations -- and says
what happens if you leave it off, with the two-worker split offered for
anyone who would rather keep the two kinds of work apart. CHANGELOG
carries it as an upgrade note, since it is something to do rather than
something that was done.

Verified in the dev stack rather than only in a test: dispatched a build
and watched worker-zips take it while the default worker stayed idle.
2026-08-26 18:06:40 -03:00

175 lines
5.4 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:-}
# The whole default stack declares one, so it comes back after a reboot
# or a Docker restart instead of half of it coming back — the confusing
# state, where the queue runs and the site is down (#1658). The dev-only
# profile services below deliberately do not: you bring those up for a
# session, not for the life of the machine.
restart: unless-stopped
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
restart: unless-stopped
depends_on:
- app
worker:
build:
context: .
dockerfile: docker/app/Dockerfile
args:
WWWUSER: ${WWWUSER:-1000}
WWWGROUP: ${WWWGROUP:-1000}
command: php artisan queue:work --queue=default --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
# Zip builds get their own worker: BuildZipDownloadJob allows itself an
# hour, and on a shared queue one large archive holds up every
# notification email behind it.
worker-zips:
build:
context: .
dockerfile: docker/app/Dockerfile
args:
WWWUSER: ${WWWUSER:-1000}
WWWGROUP: ${WWWGROUP:-1000}
command: php artisan queue:work --queue=zips --tries=1
volumes:
- .:/var/www/html
- ../packages:/var/www/packages
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
restart: unless-stopped
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
restart: unless-stopped
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: