Skip to content

repair the public/storage symlink shipped in the release tarball - #229

Merged
phanan merged 1 commit into
masterfrom
create-public-storage-symlink
Aug 10, 2026
Merged

repair the public/storage symlink shipped in the release tarball#229
phanan merged 1 commit into
masterfrom
create-public-storage-symlink

Conversation

@phanan

@phanan phanan commented Aug 10, 2026

Copy link
Copy Markdown
Member

Fixes the image storage failure reported in #224, at a different layer than that PR proposed.

The bug

koel's release archive ships public/storage as an absolute symlink into the directory the release runner built in. Straight from the v9.11.1 tarball:

koel/public/storage -> /home/runner/work/koel/koel/storage/app/public

That path exists on no machine but the GitHub Actions runner, so in the container the link dangles. koel resolves uploaded images through public_path('storage/images'), so it can neither read nor write them, and koel:doctor reports:

Image storage directory public/storage/images is not readable/writable  ERROR

Confirmed in the currently published image:

$ docker run --rm --entrypoint sh phanan/koel:9.11.1 -c 'ls -la /var/www/html/public/storage'
lrwxrwxrwx 1 www-data www-data 46 /var/www/html/public/storage -> /home/runner/work/koel/koel/storage/app/public

koel:init cannot repair it. Laravel's storage:link checks file_exists(), which is false for a dangling symlink, so it attempts to create one and fails on the path that is already there.

The fix

Replace the link at build time with a relative one to the same target, so it resolves wherever the tree is unpacked. The volume stays where the VOLUME declaration already points, so existing installations keep their images.

$ docker run --rm koel-symlink-test sh -c 'readlink -f /var/www/html/public/storage'
/var/www/html/storage/app/public

...and public/storage/images is writable by www-data, which is what koel:doctor checks.

The goss suite now asserts the symlink and its target directory, so a regression fails CI rather than reaching users.

On #224

That PR moved the volume mount to public/storage/images in docker-compose.mysql.yml, which does make the path writable, but mounts over the symlink's location — leaving the VOLUME declaration pointing at an orphaned path, moving existing installations' images to a different volume, and patching one of the three compose files. Fixing the link in the image covers every compose file and every hand-rolled docker run.

The root cause is in koel itself and is fixed separately in koel/koel#2644koel:init now creates a relative link, so future releases ship a portable archive. This change is still worth keeping: it repairs every release already published.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed broken public storage links in deployed Docker images.
    • Ensured uploaded images are accessible through the application’s public storage path.
  • Tests

    • Added deployment checks verifying the storage directory permissions, ownership, and symlink target.

The koel release archive contains public/storage as an absolute symlink into the
path the release runner built at, so it resolves nowhere in the container and koel
cannot read or write uploaded images — koel:doctor reports the image storage
directory as unwritable.

Laravel's storage:link cannot repair it either: file_exists() is false for a
dangling link, so the command tries to create one and fails on the existing path.

Replace it at build time with a relative link to the same target, and assert both
it and the image directory in the goss suite.
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Docker image now replaces the release symlink with a relative public/storage link. Goss validates the link target and the public storage directory attributes.

Changes

Public storage symlink

Layer / File(s) Summary
Deploy and validate public storage symlink
Dockerfile, goss.yaml
The deployment creates public/storage as a relative symlink to ../storage/app/public. Goss checks the symlink target and validates the image directory as mode 0755, owned by www-data:www-data.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related PRs

  • koel/docker#221: Both changes modify the Docker image’s public/storage symlink and storage paths.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: repairing the public/storage symlink in the release tarball.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch create-public-storage-symlink

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Dockerfile`:
- Around line 105-108: The Dockerfile source step must clone the Koel repository
at the declared KOEL_VERSION_REF instead of downloading a release tarball.
Update the initial application-fetch command to use ARG KOEL_VERSION_REF as the
git ref, while preserving the existing build layout and subsequent setup steps.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3744d05e-a228-49a7-b361-b6664390319e

📥 Commits

Reviewing files that changed from the base of the PR and between 9bb9360 and 785e6c7.

📒 Files selected for processing (2)
  • Dockerfile
  • goss.yaml

Comment thread Dockerfile
Comment on lines +105 to +108
# The release tarball ships public/storage as an absolute symlink into the path the
# release runner built it at, which resolves nowhere here and leaves koel unable to read
# or write uploaded images. Replace it with a relative link to the same target.
&& ln -sfn ../storage/app/public /var/www/html/public/storage \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Use the required git clone source for KOEL_VERSION_REF.

The Dockerfile still downloads a release tarball at Line 8. It does not clone Koel at the ref declared by ARG KOEL_VERSION_REF.

Change the source step to clone KOEL_VERSION_REF, or update the guideline if release tarballs are intentional.

As per coding guidelines: “The Dockerfile must use ARG KOEL_VERSION_REF=... as the git ref to clone the koel app at the specified version.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 105 - 108, The Dockerfile source step must clone the
Koel repository at the declared KOEL_VERSION_REF instead of downloading a
release tarball. Update the initial application-fetch command to use ARG
KOEL_VERSION_REF as the git ref, while preserving the existing build layout and
subsequent setup steps.

Source: Coding guidelines

@phanan
phanan merged commit dec6960 into master Aug 10, 2026
3 checks passed
@phanan
phanan deleted the create-public-storage-symlink branch August 10, 2026 20:38
@phanan

phanan commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

Correcting something I got wrong above.

I wrote that koel:init cannot repair the link because file_exists() is false for a dangling symlink, so storage:link fails on the existing path. The first half is right, the conclusion is not. Laravel's StorageLinkCommand falls through that guard and then hits an is_link() branch which deletes the link before recreating it:

if (file_exists($link) && ! $this->isRemovableSymlink($link, $this->option('force'))) {
    // skipped for a dangling link, since file_exists() is false
}

if (is_link($link)) {
    $this->laravel->make('files')->delete($link);
}

So koel:init does repair a dangling link — it just recreated it as another absolute one, which is why the problem kept coming back. The reason the published image is broken is simpler than I said: the image is built without ever running koel:init, so nothing repairs the link that came out of the tarball, and koel:init is skipped at runtime when no .env is mounted or SKIP_INIT is set.

None of this changes the fix or the diagnosis of the underlying bug — the archive genuinely ships a link into the release runner's filesystem. I just described the repair path incorrectly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant