Problem
The image does not contain Flarum. Dockerfile has zero references to flarum/flarum — it is a PHP 8.3 + nginx + Composer runtime plus a boot script. The forum is resolved from Packagist on first container start, and nothing about that resolution is pinned:
| Where |
What |
Constraint |
entrypoint.sh:186 |
flarum/flarum |
^2.0 with --stability=beta |
entrypoint.sh:274 |
fof/upload |
* |
entrypoint.sh:275 |
fof/redis |
* |
entrypoint.sh:276 |
fof/horizon |
* |
entrypoint.sh:277 |
flarum/realtime |
* |
entrypoint.sh:280 |
flarum/audit |
* |
entrypoint.sh:281 |
flarum/extension-manager |
* |
There is no composer.json or composer.lock in the repository, so nothing records what a given image is supposed to install.
What that costs
- The same image tag produces different forums. Seven floating constraints, one of them accepting prereleases. Two people running
docker compose up a week apart get different code, and so does the same person redeploying.
docker pull gets you no Flarum. First boot needs working Packagist access. An outage, a yanked release, or a network-restricted host means no install — and there is no offline or airgapped path at all.
- Image tags cannot express a Flarum version.
:1.0.0 versions the wrapper. There is no way for a user to pin the thing they actually care about, and no way for us to say "this image is Flarum 2.0.3".
- No rollback. Rebuilding a previous tag does not reproduce the previous forum, because resolution happens at run time, not build time.
- A broken upstream release breaks new installs retroactively, including installs of images that were fine yesterday.
- The README oversells it. It says "There is no runtime download of a setup script". Literally true — no setup script is fetched — but the entire application and six extensions are. A reader comparing images will take that sentence to mean the opposite of what happens.
Proposal — pinned skeleton baked at build time
Same approach already used for the Warble tenant images: resolve once, commit the lockfile, bake the result, copy into the volume on first boot.
- Commit
composer.json + composer.lock describing Flarum plus the six bundled extensions, at explicit versions.
- Build time:
composer install --no-dev from that lockfile into /opt/flarum-skeleton. The image now genuinely contains Flarum.
- First boot: if the data volume is empty, copy the skeleton into
/var/www/html and continue with the existing install path (flarum install, migrations, enable extensions). If the volume already has a forum, do nothing — same as today.
- Tagging: image tags become
<flarum-version>-rN (e.g. 2.0.3-r0), so the tag states what you get. TARGET_FLARUM_VERSION stays as an escape hatch for people who want to float deliberately.
- Updating: bump the lockfile in a PR.
roundtrip proves the new resolution installs and restores before it ships, which is strictly better than discovering it in production.
The Extension Manager keeps working: the volume is still a writable Composer project with a real composer.json, it is only seeded from the image instead of resolved from the network.
Acceptance criteria
Risks / open questions
- Image size grows by the vendor tree (tens of MB). Acceptable, and the tradeoff should be stated in the README rather than hidden.
- Upgrade path for existing deployments needs deciding: does a user on a running volume get the new skeleton on image update, or stay put until they act? Staying put is safer and matches current behaviour, but means the image version and the forum version can diverge — which partly re-opens the problem this issue exists to close.
- Lockfile drift — worth a scheduled job or Renovate to open the bump PR, otherwise the pin quietly becomes staleness.
--stability=beta should not survive this change unless there is a reason; if Flarum 2.0 stable is out, pin stable.
Non-goals
- Not changing how extensions are enabled, how
extend.php is generated, or the backup/restore flow.
- Not removing
TARGET_FLARUM_VERSION.
- Not bundling additional extensions.
Problem
The image does not contain Flarum.
Dockerfilehas zero references toflarum/flarum— it is a PHP 8.3 + nginx + Composer runtime plus a boot script. The forum is resolved from Packagist on first container start, and nothing about that resolution is pinned:entrypoint.sh:186flarum/flarum^2.0with--stability=betaentrypoint.sh:274fof/upload*entrypoint.sh:275fof/redis*entrypoint.sh:276fof/horizon*entrypoint.sh:277flarum/realtime*entrypoint.sh:280flarum/audit*entrypoint.sh:281flarum/extension-manager*There is no
composer.jsonorcomposer.lockin the repository, so nothing records what a given image is supposed to install.What that costs
docker compose upa week apart get different code, and so does the same person redeploying.docker pullgets you no Flarum. First boot needs working Packagist access. An outage, a yanked release, or a network-restricted host means no install — and there is no offline or airgapped path at all.:1.0.0versions the wrapper. There is no way for a user to pin the thing they actually care about, and no way for us to say "this image is Flarum 2.0.3".Proposal — pinned skeleton baked at build time
Same approach already used for the Warble tenant images: resolve once, commit the lockfile, bake the result, copy into the volume on first boot.
composer.json+composer.lockdescribing Flarum plus the six bundled extensions, at explicit versions.composer install --no-devfrom that lockfile into/opt/flarum-skeleton. The image now genuinely contains Flarum./var/www/htmland continue with the existing install path (flarum install, migrations, enable extensions). If the volume already has a forum, do nothing — same as today.<flarum-version>-rN(e.g.2.0.3-r0), so the tag states what you get.TARGET_FLARUM_VERSIONstays as an escape hatch for people who want to float deliberately.roundtripproves the new resolution installs and restores before it ships, which is strictly better than discovering it in production.The Extension Manager keeps working: the volume is still a writable Composer project with a real
composer.json, it is only seeded from the image instead of resolved from the network.Acceptance criteria
Dockerfilereferencesflarum/flarum;docker create+docker exportshows Flarum present with no container ever startedcomposer.lockhashes in the volumeroundtripstill passes, including restoring an older backup into the newer versionRisks / open questions
--stability=betashould not survive this change unless there is a reason; if Flarum 2.0 stable is out, pin stable.Non-goals
extend.phpis generated, or the backup/restore flow.TARGET_FLARUM_VERSION.