# 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.md` for 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 `.env` file 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

```bash
docker compose build app
```

The build performs the following steps:

1. Installs Node dependencies and runs `npm run build` to produce production assets.
2. Installs PHP dependencies with Composer (`--no-dev --no-scripts`).
3. Creates a PHP 8.3 FPM image with required extensions (GD, intl, Redis, etc.).
4. 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:

```bash
cp docker/.env.docker docker/.env.docker.local
```

Set (at minimum):

- `APP_KEY` — generate with `docker compose run --rm app php artisan key:generate --show`.
- Database credentials (`DB_*`). The provided MySQL service defaults to `fotospiel/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

```bash
docker compose up -d
```

Services started:

- `app`: PHP-FPM container serving the Laravel application.
- `web`: Nginx proxy forwarding requests to PHP-FPM on port `8080` by default (`APP_HTTP_PORT`).
- `queue` & `media-storage-worker`: queue consumers (default + media archival).
- `scheduler`: runs `php artisan schedule:work`.
- `horizon` (optional, disabled unless `--profile horizon` is supplied).
- `redis` & `mysql`.

### Migrations & seeds

Run once after the first boot or when deploying new schema changes:

```bash
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 `/scripts/` inside the container (copied from the repository’s `scripts/` folder). The Compose services mount the same application volume so code stays in sync. Adjust concurrency by scaling services:

```bash
docker compose up -d --scale queue=2 --scale media-storage-worker=2
```

To enable Horizon (dashboard, smart balancing):

```bash
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 the `storage` directory 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

1. `git pull` the repository (or deploy your release branch).
2. `docker compose build app`.
3. `docker compose up -d`.
4. Run migrations + seeders if required.
5. 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 `web` container.
- Point `APP_URL` to your public domain and enable trusted proxies.
- Externalize MySQL/Redis to managed services for better resilience.
- Configure backups for the `storage` directories 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` (runs `npm ci && npm run build` in `docs/site/` and outputs to `docs/site/build`).
- The Nginx container mounts that build directory and serves it at `/internal-docs/`. History fallback is already configured in `docker/nginx/default.conf`.
- Access is protected with HTTP Basic Auth. Update `docker/nginx/.htpasswd-docs` with 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/build` as 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.
