Worker::runAll() parses start/stop/restart/... off the real \$argv on Linux - which here is Symfony console's own arguments (bin/console app:puzzle-relay), not a command it recognizes, so it printed its usage text and exited on every boot, and Docker kept restarting it forever. Windows skips that argv parsing entirely, which is why this never showed up locally. Worker::\$command = 'start' is Workerman's own mechanism for exactly this "used as a library" case - it appends to what getArgv() returns without touching the real \$argv. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
dmtools
DM tools for the Monday-evening D&D campaign.
Symfony 8 (PHP 8.5) web app with a Bootstrap 5 + DataTables front end served through Symfony AssetMapper (no Node build step). Behind a form-login gate; there is no self-registration.
- Not run in Docker locally (Windows dev box uses the Symfony CLI + local MariaDB).
- Is Dockerized on the testing and live servers, behind the shared Nginx
Proxy Manager at
https://dmtools.fvandenberg.nl.
Repo: https://gitea.fvandenberg.nl/Frank/dmtools.git
Local development (dev box)
Prerequisites: PHP 8.5 at C:\php (on PATH so the Symfony CLI discovers it —
symfony local:php:list should show 8.5), the Symfony CLI, Composer, and a local
MariaDB/MySQL (WAMP is fine). .php-version pins this directory to PHP 8.5.
# 1. Point the app at your local database + dev mode
cp .env .env.local
# then edit .env.local:
# APP_ENV=dev
# DATABASE_URL="mysql://root:@127.0.0.1:3307/dmtools?serverVersion=10.10.2-MariaDB&charset=utf8mb4"
# 2. Install and build
symfony composer install
symfony console doctrine:database:create
symfony console doctrine:migrations:migrate -n
symfony console importmap:install
# 3. First user, then run
symfony console app:user:create you@example.com --admin
symfony serve -d
symfony open:local
serverVersion must match your local engine (SELECT VERSION();).
Servers (testing + live)
Docker only. docker/setup.sh pins the compose project name to dmtools
(containers dmtools-php, dmtools-nginx, dmtools-db).
git pull
cp .env .env.local # first time only
# edit .env.local: real APP_SECRET (or let setup.sh generate it),
# DB_PASSWORD, DB_ROOT_PASSWORD, and NGINX_PORT / DB_HOST_PORT if 8085 / 3310 clash
./docker/setup.sh
docker compose -p dmtools exec php php bin/console app:user:create you@example.com --admin
Ports (bound to 127.0.0.1 on the host): nginx 8085 → :80, db 3310 → :3306.
Reverse proxy (Nginx Proxy Manager)
Add a proxy host: dmtools.fvandenberg.nl → forward to host dmtools-nginx,
port 80, scheme http; request a Let's Encrypt certificate.
NPM must be able to resolve dmtools-nginx, i.e. be on the dmtools_default
network. setup.sh connects it (nginx_app_1 by default; override with
NPM_CONTAINER=...); to do it by hand:
docker network connect dmtools_default nginx_app_1
NPM keeps that connection across restarts; it's only needed again after a
docker compose down drops the network.
Common commands
docker compose -p dmtools logs -f php
docker compose -p dmtools exec php bash
docker compose -p dmtools exec php php bin/console doctrine:migrations:migrate
./docker/restart.sh # full down + bring-up (no rebuild)
./docker/setup.sh --down # stop the stack
Layout notes
table.datatablein any Twig template is auto-initialised as a DataTable (assets/app.js). Opt out withdata-datatable="false"; pass options as JSON indata-datatable-options.assets/vendor/andpublic/assets/are generated (git-ignored);importmap:installon the server re-downloads vendored packages, so the servers need outbound HTTPS to jsDelivr at deploy time.