Files
ampache/PLAN.md
casjay 6d8b3f0ae3 Add CLAUDE.md and PLAN.md for ampache migration
Documents the canonical repo template spec (CLAUDE.md, copied from the
project-wide TEMPLATE.md) and the concrete migration plan for the ampache
service stack: Apache + PHP-FPM (php84) + MariaDB-server bundled with the
Ampache 7.9.3 web app prebuilt zip from upstream.
2026-05-09 18:56:18 -04:00

164 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ampache migration plan
## Service intent
Self-hosted music streaming server. Single Alpine-based Docker image bundling Apache (httpd) + PHP-FPM (php84) + MariaDB-server + the Ampache web app at `/usr/local/share/ampache`. Apache serves the app's `public/` subfolder (the canonical Ampache web root since v5+). Container exposes `:80`; first run takes the user to `install.php` which provisions the database and creates an admin account. Volumes: `/config` (user-editable settings: ampache config, httpd conf snippets, php-fpm pool, my.cnf, secure/auth) and `/data` (mariadb datadir, logs, uploaded media library if mounted there).
## Service stack
- Web server: `apache2` (Alpine) -> `/usr/sbin/httpd`, main config `/etc/apache2/httpd.conf`, vhost include in `/etc/apache2/conf.d/*.conf`. Uses event MPM, mod_rewrite + AllowOverride All for the Ampache `.htaccess`.
- App runtime: `php84-fpm` (Alpine) -> `/usr/sbin/php-fpm84`, conf `/etc/php84/php-fpm.conf` + pool `/etc/php84/php-fpm.d/www.conf`. Apache talks to it via `proxy_fcgi` over a unix socket `/run/php-fpm/php-fpm.sock` (no exposed TCP).
- Database: `mariadb` + `mariadb-client` (Alpine) -> `/usr/bin/mariadbd`, datadir `/data/db/mariadb`, socket `/run/mysqld/mysqld.sock`. Started by a separate `09-mariadb.sh` so it's up before ampache is rendered.
- Application: Ampache 7.9.3 (`ampache-7.9.3_all_php8.4.zip` from upstream GitHub releases — pre-built, vendor/ + node_modules/ already populated, no composer/npm needed at build time). Installed at `/usr/local/share/ampache`; Apache `DocumentRoot` is `/usr/local/share/ampache/public`.
## Packages (PACK_LIST / ENV_PACKAGES)
Trimmed from the original kitchen-sink list in the existing Dockerfile. Each package is on `pkgs.alpinelinux.org` (verified for the `edge` branch).
System glue:
- `bash`, `tini`, `curl`, `wget`, `unzip`, `tzdata`, `ca-certificates`, `pwgen` — entrypoint, fetching/extracting the ampache zip, password generation.
- `tar`, `gzip` — archive handling.
Apache:
- `apache2`, `apache2-ctl`, `apache2-utils` — server binary, control script, htpasswd/etc.
- `apache2-ssl` — mod_ssl (TLS support; off by default but available).
- `apache2-proxy` — mod_proxy + mod_proxy_fcgi (required to forward PHP requests to php-fpm).
- `apache2-http2` — http/2 support.
- `apache2-brotli` — mod_brotli compression.
- `apache2-icons`, `apache2-error` — icons + multilingual error pages.
(Dropped from prior list: `apache2-lua` `apache2-ldap` `apache2-webdav` `apache2-mod-wsgi` `apache-mod-fcgid` `apache2-proxy-html` — none are needed for ampache and `apache2-proxy` is the canonical fcgi backend, not `mod_fcgid`.)
PHP 8.4 — only the modules Ampache actually requires per upstream docs (PDO, PDO_MYSQL, hash, session, intl, json, curl, simplexml + optional gd, ldap, zip), plus FPM and the bare runtime:
- `php84` `php84-fpm` `php84-common` `php84-ctype` `php84-pdo` `php84-pdo_mysql` `php84-mysqli` `php84-mysqlnd` `php84-session` `php84-intl` `php84-curl` `php84-simplexml` `php84-xml` `php84-xmlreader` `php84-xmlwriter` `php84-dom` `php84-mbstring` `php84-iconv` `php84-tokenizer` `php84-fileinfo` `php84-openssl` `php84-phar` `php84-gd` `php84-zip` `php84-bz2` `php84-gmp` `php84-exif` `php84-opcache` `php84-pecl-redis`
(Dropped from prior list: dba, dev, doc, embed, enchant, ffi, ftp, gettext, imap, ldap (kept off — not needed by ampache out-of-box), litespeed, odbc, pcntl, pdo_dblib, pdo_odbc, pdo_pgsql, pdo_sqlite, pear, pgsql, phpdbg, posix, pspell, shmop, snmp, soap, sockets, sodium, sqlite3, sysvmsg, sysvsem, sysvshm, tidy, xsl, pecl-memcached, pecl-mcrypt, pecl-mongodb, calendar, cgi, bcmath. Kept what either a stock Ampache install touches or is part of ampache's bundled vendor extensions. `composer` is dropped — we use the prebuilt zip, no install step needed.)
MariaDB:
- `mariadb` — server (`/usr/bin/mariadbd`).
- `mariadb-client``mariadb` CLI client used by the post-execute initdb step.
- `mariadb-server-utils``mysql_install_db`, `mariadb-admin`, `mariadb-secure-installation`.
## Configs to ship in rootfs/tmp/etc/
Wipe-and-replace at build time (per template §4). All paths under `rootfs/tmp/etc/`.
- `apache2/httpd.conf` — minimal Alpine apache main config: load only the modules we need (mpm_event, mime, dir, alias, authz_core, authz_host, autoindex, deflate, brotli, expires, headers, log_config, mime_magic, negotiation, proxy, proxy_fcgi, rewrite, setenvif, ssl, status, unixd, http2). User/Group `apache:apache`. ServerRoot `/var/www`. Logs to `/data/logs/apache2/{access,error}.log`. PidFile `/run/apache2/httpd.pid`. ErrorLog/LogLevel sensible defaults. Final line: `IncludeOptional /etc/apache2/conf.d/*.conf` and `IncludeOptional /config/apache2/vhosts.d/*.conf` (optional, so empty dir doesn't crash).
- `apache2/conf.d/ampache.conf` — vhost: `<VirtualHost *:80>` with `DocumentRoot /usr/local/share/ampache/public`, `<Directory>` block (`Options FollowSymLinks`, `AllowOverride All`, `Require all granted`), `ProxyPassMatch ^/(.+\.php(/.*)?)$ unix:/run/php-fpm/php-fpm.sock|fcgi://localhost/usr/local/share/ampache/public/$1`, `DirectoryIndex index.php index.html`. Sets `SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1` (per upstream Apache vhost, needed for ampache API auth headers). ErrorLog/CustomLog under `/data/logs/apache2/`.
- `apache2/conf.d/mpm.conf` — switch to mpm_event explicitly + sane worker tunings.
- `php84/php.ini` — production-tuned: `memory_limit = 512M`, `upload_max_filesize = 256M`, `post_max_size = 256M`, `max_execution_time = 300`, `date.timezone = ${TZ}`, `expose_php = Off`, `cgi.fix_pathinfo = 0`, `opcache.enable = 1`, `opcache.memory_consumption = 256`, `opcache.max_accelerated_files = 20000`, `session.save_path = /tmp/php-sessions`.
- `php84/php-fpm.conf` — global: `pid = /run/php-fpm/php-fpm.pid`, `error_log = /data/logs/php-fpm/error.log`, `daemonize = no` (we run under our own supervisor), `include=/etc/php84/php-fpm.d/*.conf`.
- `php84/php-fpm.d/www.conf` — pool `[www]`, `user = apache`, `group = apache`, `listen = /run/php-fpm/php-fpm.sock`, `listen.owner = apache`, `listen.group = apache`, `listen.mode = 0660`, `pm = dynamic`, `pm.max_children = 20`, `pm.start_servers = 4`, `pm.min_spare_servers = 2`, `pm.max_spare_servers = 8`, `pm.max_requests = 500`, `clear_env = no`.
- `my.cnf.d/mariadb-server.cnf` — server config: `[mysqld]` with `datadir = /data/db/mariadb`, `socket = /run/mysqld/mysqld.sock`, `bind-address = 127.0.0.1` (DB only reachable inside container), `port = 3306`, `character-set-server = utf8mb4`, `collation-server = utf8mb4_unicode_ci`, `max_allowed_packet = 64M`, `innodb_buffer_pool_size = 256M`, `log_error = /data/logs/mariadb/mariadb.err.log`, `pid-file = /run/mysqld/mariadb.pid`. `[client]` `socket = /run/mysqld/mysqld.sock`. `[mysqld_safe]` matching log path.
- `ampache/ampache.cfg.php.dist` — empty placeholder (or a copy of the ampache-shipped dist file). Real `ampache.cfg.php` is generated by `install.php` on first web visit.
## /config/<svc>/ layout (user-editable)
The framework's `__initialize_system_etc` symlinks every file under `/config/<svc>/` back to its `/etc/<svc>/` peer. So our `/config/` seed (via `template-files/config/`) mirrors `/etc/` with the same paths:
- `/config/apache2/httpd.conf` -> symlinked to `/etc/apache2/httpd.conf`
- `/config/apache2/conf.d/ampache.conf` -> symlinked to `/etc/apache2/conf.d/ampache.conf`
- `/config/apache2/conf.d/mpm.conf` -> symlinked to `/etc/apache2/conf.d/mpm.conf`
- `/config/apache2/vhosts.d/*.conf` -> picked up by the `IncludeOptional` line for user-supplied vhosts
- `/config/php84/php.ini` -> `/etc/php84/php.ini`
- `/config/php84/php-fpm.conf` -> `/etc/php84/php-fpm.conf`
- `/config/php84/php-fpm.d/www.conf` -> `/etc/php84/php-fpm.d/www.conf`
- `/config/my.cnf.d/mariadb-server.cnf` -> `/etc/my.cnf.d/mariadb-server.cnf`
- `/config/ampache/ampache.cfg.php` -> bind-mounted into `/usr/local/share/ampache/config/ampache.cfg.php` (created post-install by `install.php`; we symlink the live file out to `/config/ampache/` after install completes so it survives container recreation).
- `/config/secure/auth/{root,user}/{ampache,mariadb}_{name,pass}` -> generated/used by the framework
- `/config/env/{ampache,mariadb}.sh` -> per-service env overrides
ADDITIONAL_CONFIG_DIRS for ampache will be `/config/apache2 /config/php84 /config/my.cnf.d /config/ampache` so each one runs through `__initialize_system_etc`.
## init.d/99-ampache.sh (and 09-mariadb.sh)
Two init.d scripts. MariaDB starts first (`09-`), Apache+PHP-FPM start under ampache (`99-`).
`rootfs/usr/local/etc/docker/init.d/09-mariadb.sh` — copy of mariadb repo's `09-mariadb.sh` with these knobs:
- `SERVICE_NAME="mariadb"`, `EXEC_CMD_BIN='mariadbd'`, `EXEC_CMD_ARGS='--user=$SERVICE_USER --datadir=$DATABASE_DIR --socket=/run/mysqld/mysqld.sock'`
- `SERVICE_USER="mysql"`, `SERVICE_GROUP="mysql"`
- `IS_DATABASE_SERVICE="yes"`, `DATABASE_SERVICE_TYPE="mariadb"`
- `__run_pre_execute_checks_local`: bootstrap datadir if missing (`mysql_install_db --datadir=$DATABASE_DIR --user=mysql`).
- `__post_execute_local`: when first run, create the `ampache` database + `ampache` user + grant; write password to `/config/secure/auth/user/ampache_db_pass`.
`rootfs/usr/local/etc/docker/init.d/99-ampache.sh` — based on nginx's 99-nginx.sh structure:
- `SERVICE_NAME="ampache"`, `SERVICE_USER="apache"`, `SERVICE_GROUP="apache"`
- `EXEC_CMD_BIN='httpd'`, `EXEC_CMD_ARGS='-D FOREGROUND -f /etc/apache2/httpd.conf'`
- `EXEC_PRE_SCRIPT='/usr/sbin/php-fpm84'` — start php-fpm as the pre-script (it reads `/etc/php84/php-fpm.conf` with `daemonize=no` but we run it before httpd; the framework's EXEC_PRE_SCRIPT pattern handles this).
- `IS_WEB_SERVER="yes"`, `USES_DATABASE_SERVICE="yes"`, `DATABASE_SERVICE_TYPE="mariadb"`
- `WWW_ROOT_DIR="/usr/local/share/ampache/public"`, `ETC_DIR="/etc/apache2"`, `CONF_DIR="/config/apache2"`
- `ADDITIONAL_CONFIG_DIRS="/config/php84 /config/my.cnf.d /config/ampache"`
- `SERVICE_PORT="80"`
- `__execute_prerun_local`: ensure runtime dirs (`/run/apache2`, `/run/php-fpm`, `/run/mysqld`, `/tmp/php-sessions`, `/data/logs/{apache2,php-fpm,mariadb,ampache}`) exist with right ownership; chown `/usr/local/share/ampache/{config,channel,rest,play}` to `apache:apache` for the install.php writes; symlink `/usr/local/share/ampache/config/ampache.cfg.php` -> `/config/ampache/ampache.cfg.php` if not present.
- `__pre_execute_local`: wait for mariadb socket to appear (`/run/mysqld/mysqld.sock`) up to 30s before starting apache.
- `__update_conf_files_local`: replace `REPLACE_TZ` token in `/etc/php84/php.ini` with `$TZ`. (No tokens in apache or my.cnf — paths are baked in.)
## 05-custom.sh additions
Replace the current placeholder content (which only `mkdir`s and creates an empty webapp dir) with:
1. Wipe distro-default `/etc/{apache2,php84,my.cnf.d}/*` (drop conf.d/{default,info,languages,mpm,userdir}.conf, ssl.conf etc.) so only our shipped files remain after `cp -Rf /tmp/etc/...`.
```sh
for d in apache2 php84 my.cnf.d; do
[ -d /tmp/etc/$d ] || continue
rm -Rf /etc/$d/*
cp -Rf /tmp/etc/$d/. /etc/$d/
done
```
2. Fetch + install Ampache 7.9.3 prebuilt zip:
```sh
AMPACHE_VERSION="7.9.3"
AMPACHE_URL="https://github.com/ampache/ampache/releases/download/${AMPACHE_VERSION}/ampache-${AMPACHE_VERSION}_all_php8.4.zip"
mkdir -p /usr/local/share/ampache
cd /tmp
wget -q -O /tmp/ampache.zip "$AMPACHE_URL"
unzip -q /tmp/ampache.zip -d /usr/local/share/ampache
rm -f /tmp/ampache.zip
chown -R apache:apache /usr/local/share/ampache
# ensure config dir exists for install.php to drop ampache.cfg.php
mkdir -p /usr/local/share/ampache/config
chown apache:apache /usr/local/share/ampache/config
```
3. Create runtime dirs needed by apache/php-fpm/mariadb so the first start doesn't trip on missing parents:
```sh
mkdir -p /run/apache2 /run/php-fpm /run/mysqld /var/log/apache2 /tmp/php-sessions
```
4. Stage seed `template-files/config/ampache/` with an empty `.gitkeep` so `__initialize_config_dir` creates `/config/ampache/` on first run (the real `ampache.cfg.php` is written by install.php).
## 04-users.sh additions
The `mariadb` Alpine package creates the `mysql` user automatically; the `apache2` package creates `apache`. So 04-users.sh stays mostly empty — but add a defensive `addgroup -S apache 2>/dev/null; adduser -S -G apache -h /var/www -s /sbin/nologin apache 2>/dev/null` block in case package ordering doesn't guarantee them.
## 02-packages.sh additions
Empty (no per-package compile or pip step needed; the prebuilt ampache zip has all PHP deps inside `vendor/`).
## Dockerfile changes
- Update `BUILD_DATE` to `202605091200` (today, 2026-05-09 12:00).
- Replace `PACK_LIST` with the trimmed list above.
- Keep everything else (multi-stage, scratch final, ARGs, ENVs, volumes, healthcheck).
- No structural changes — the template's RUN steps already invoke 0007 setup scripts in order.
## .env.scripts changes
- Sync `ENV_PACKAGES` to match the new `PACK_LIST` (single space separated, no double spaces).
- Leave `SERVICE_PORT="80"`, `EXPOSE_PORTS=""`, `PHP_VERSION="php84"`.
## README updates
Document the first-run workflow: visit `http://localhost:8080/` → ampache installer → fill in DB host `127.0.0.1`, user `ampache`, password from `/config/secure/auth/user/ampache_db_pass` (or read from `docker exec`), DB name `ampache` (already created by post-execute), web admin → enter desired admin user/email/pass → done. Note the volumes (`/config`, `/data`) and the music library mount pattern (`-v /path/to/music:/media:ro`).
## Verification (success criteria)
1. `cd /root/Projects/github/casjaysdevdocker/ampache && rm -f .build_failed && buildx run Dockerfile` succeeds for both `linux/amd64` and `linux/arm64`. Single retry permitted on transient network errors.
2. `docker run -d --rm --name test-ampache -p 18080:80 docker.io/casjaysdevdocker/ampache:latest` boots; after ~60s, `docker logs test-ampache | tail -50` shows no fatal errors and shows mariadb + php-fpm + httpd all started (look for "ready for connections", "fpm is running", "Apache/2... configured -- resuming normal operations").
3. `curl -fsS -o /dev/null -w '%{http_code}' http://localhost:18080/` returns 200 or 302 (the install.php redirect).
4. `docker exec test-ampache ls /config/ampache/ /config/apache2/ /config/php84/ /data/db/mariadb/ /usr/local/share/ampache/public/index.php` — every path exists.
5. `docker exec test-ampache mariadb -u root -e 'SHOW DATABASES;'` lists `ampache`.
6. `docker stop test-ampache`.
## Rollback
If anything in this PLAN.md proves wrong, the existing files are recoverable from git (`git checkout -- rootfs/`). New files (init.d, tmp/etc) can be removed cleanly because they didn't exist before.