mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-11 22:38:54 +00:00
202a1d7ad5
#1658 reports that app, web, db and redis have no restart policy, so the
stack does not come back after a reboot. True, and fixed here — but the
file it is about is the development stack, and the production example has
had the policy all along. The reporter got there by following DOCKER.md,
which is #1627 again: 745f24c fixed the README's pointer and left this
page's body describing a stack no user should be running.
Against an image install almost every procedure on it was wrong. It said
uploads live in `storage/app/files/` "in the project directory" and `.env`
beside it — both are on the storage volume, and the entrypoint generates
that `.env` itself. Its compose.override.yaml recipe bind-mounted into
app, web, worker and scheduler, which are one container under supervisord
in the image, at a path one level too deep to carry APP_KEY. It told
people to chown a directory the entrypoint already chowns, to rsync from a
host path that does not exist, and to `git pull` to upgrade. Its mysqldump
read ${DB_ROOT_PASSWORD} from a .env an image install does not have, so
the documented backup silently fell back to `root` and failed. Docker Hub
links this page as "where your data lives, backups, moving to another
server".
So it is now about the image, and shorter for it: two volumes instead of
three loose things, the key explained where people actually lose it, no
override file because the compose file is the operator's own, and a
reboot section — the answer to the issue for anyone who wrote their own
compose. The clone-and-build stack keeps one pointer to CONTRIBUTING.md,
which has been the correct place for it since #1627.
The Docker Hub page keeps the two facts a reader who never leaves it
needs and hands off the procedures, so the drift that caused this has one
copy to go wrong instead of two.
Adminer and mailpit stay without a restart policy on purpose: those come
up for a session, not for the life of the machine.
Refs #1658
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
154 lines
4.7 KiB
YAML
154 lines
4.7 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 --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
|
|
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:
|