6.1 KiB
Docker Deployment Guide
This guide describes the recommended, repeatable way to run the Fotospiel platform in Docker for production or high-fidelity staging environments. It pairs a multi-stage build (PHP-FPM + asset pipeline) with a Compose stack that includes Nginx, worker processes, Redis, and MySQL.
Dokploy users: see
docs/ops/deployment/dokploy.mdfor service definitions, secrets, and how to wire the same containers (web, queue, scheduler, vsftpd) inside Dokploy. That document builds on the base Docker instructions below.
1. Prerequisites
- Docker Engine 24+ and Docker Compose v2.
- A
.envfile for the application (see step 4). - Optional: an external MySQL/Redis if you do not want to run the bundled containers.
2. Build the application image
docker compose build app
The build performs the following steps:
- Installs Node dependencies and runs
npm run buildto produce production assets. - Installs PHP dependencies with Composer (
--no-dev --no-scripts). - Creates a PHP 8.3 FPM image with required extensions (GD, intl, Redis, etc.).
- Stores the compiled application under
/opt/app; the runtime entrypoint syncs it into the shared volume when a container starts.
3. Configure environment
Copy the sample Docker environment file and edit the secrets:
cp docker/.env.docker docker/.env.docker.local
Set (at minimum):
APP_KEY— generate withdocker compose run --rm app php artisan key:generate --show.- Database credentials (
DB_*). The provided MySQL service defaults tofotospiel/secret. STORAGE_ALERT_EMAIL— recipient for upload failure alerts (optional).
Point docker-compose.yml to the file you created by either renaming it to docker/.env.docker or adjusting the env_file entries.
4. Boot the stack
docker compose up -d
Services started:
app: PHP-FPM container serving the Laravel application.web: Nginx proxy forwarding requests to PHP-FPM on port8080by default (APP_HTTP_PORT).queue&media-storage-worker: queue consumers (default + media archival).scheduler: runsphp artisan schedule:work.horizon(optional, disabled unless--profile horizonis supplied).redis&mysql.
Migrations & seeds
Run once after the first boot or when deploying new schema changes:
docker compose exec app php artisan migrate --force
docker compose exec app php artisan db:seed --class=MediaStorageTargetSeeder --force
If you already have data, skip the seeder or seed only new records.
5. Queue & Horizon management
Worker entrypoints live in docs/queue-supervisor/. The Compose services mount the same application volume so code stays in sync. Adjust concurrency by scaling services:
docker compose up -d --scale queue=2 --scale media-storage-worker=2
To enable Horizon (dashboard, smart balancing):
docker compose --profile horizon up -d horizon
6. Scheduler & cron jobs
The compose stack ships a scheduler service that runs php artisan schedule:work, so all scheduled commands defined in App\Console\Kernel stay active. For upload health monitoring, keep the helper script from cron/upload_queue_health.sh on the host (or inside a management container) and add a cron entry:
*/5 * * * * /var/www/html/cron/upload_queue_health.sh
This wrapper logs to storage/logs/cron-upload-queue-health.log and executes php artisan storage:check-upload-queues, which in turn issues guest-facing upload alerts when queues stall or fail repeatedly. In containerised environments mount the repository so the script can reuse the same PHP binary as the app, or call the artisan command directly via docker compose exec app php artisan storage:check-upload-queues.
The dashboard becomes available at /horizon and is protected by the Filament super-admin auth guard.
6. Persistent data & volumes
app-code— contains the synced application, including thestoragedirectory and generated assets.mysql-data— MySQL data files.redis-data— Redis persistence (disabled by default; change the Redis command if you want AOF snapshots).
Back up the volumes before upgrades to maintain tenant media and database state.
7. Updating the stack
git pullthe repository (or deploy your release branch).docker compose build app.docker compose up -d.- Run migrations + seeders if required.
- Check logs:
docker compose logs -f app queue media-storage-worker.
Because the app image keeps the authoritative copy of the code, each container restart rsyncs fresh sources into the shared volume ensuring reliable updates without lingering artefacts.
8. Production hardening
- Terminate TLS with a dedicated reverse proxy (Traefik, Caddy, AWS ALB, etc.) in front of the
webcontainer. - Point
APP_URLto your public domain and enable trusted proxies. - Externalize MySQL/Redis to managed services for better resilience.
- Configure backups for the
storagedirectories and database dumps. - Hook into your observability stack (e.g., ship container logs to Loki or ELK).
9. Internal docs publishing
- Build the static docs site locally or in CI via
./scripts/build-docs-site.sh(runsnpm ci && npm run buildindocs/site/and outputs todocs/site/build). - The Nginx container mounts that build directory and serves it at
/internal-docs/. History fallback is already configured indocker/nginx/default.conf. - Access is protected with HTTP Basic Auth. Update
docker/nginx/.htpasswd-docswith real credentials (htpasswd -c docker/nginx/.htpasswd-docs <user>). The repo ships with a weak default for local use—never reuse it in production. - Optionally run the build script inside your CI pipeline before deploying so the static output is always up to date. A typical GitHub Actions job would simply call the script after checkout and upload
docs/site/buildas an artifact or rsync it to the server.
With the provided configuration you can bootstrap a consistent Docker-based deployment across environments while keeping queue workers, migrations, and asset builds manageable. Adjust service definitions as needed for staging vs. production.