fix(docker): recreate storage directories on container start (#702)

Fixes InvoiceShelf/docker#75 and #69, and gives InvoiceShelf/docker#77
and #63 an actionable error instead of a cryptic one.

storage/framework/{cache,sessions,views}, storage/logs and storage/app
hold no tracked content — only .gitignore stubs — so nothing guarantees
they exist inside a mounted volume. Docker seeds a named volume from the
image exactly once, when the volume is empty, and never again: a volume
created by an older image keeps whatever it had through every subsequent
upgrade. When those directories are absent Laravel dies at boot with
"Please provide a valid cache path", because config/view.php resolves its
compiled path with realpath(), which returns false for a missing
directory. The sqlite branch also cannot place its database.

Reproduced against a locally built image: deleting storage/framework from
a named volume fails the container with exactly that message, and passes
with this change.

The chown is guarded on being root. The image runs as www-data (uid 82),
where chown of a foreign-owned file is EPERM and, under `set -e`, would
stop the container from starting at all — which is the likely reason it
was dropped from this tree previously. Guarding it keeps the benefit for
anyone running as root without that failure mode.

A mount the container genuinely cannot write to is not something the
entrypoint can fix, so it now says so and names the remedy, rather than
letting the failure surface later as a Laravel stack trace.
This commit is contained in:
Darko Gjorgjijoski
2026-07-29 12:20:28 +02:00
committed by GitHub
parent 552da3ca84
commit ae035498c6

View File

@@ -12,6 +12,32 @@ InvoiceShelf Version: $version
cd /var/www/html
# These carry no tracked content — only .gitignore stubs — so a mount over
# storage/ can arrive without them, and Laravel then dies at boot with "Please
# provide a valid cache path" (config/view.php resolves its compiled path with
# realpath(), which returns false for a missing directory). Recreate them before
# anything writes there, including the sqlite database placed in storage/app
# below. See InvoiceShelf/docker#75, #69 and #77.
echo "**** Ensuring storage directories exist ****"
if ! mkdir -p \
storage/app/public \
storage/framework/cache/data \
storage/framework/sessions \
storage/framework/views \
storage/logs \
bootstrap/cache 2>/dev/null; then
echo "!!!! Cannot write to /var/www/html/storage."
echo "!!!! This container runs as uid $(id -u) (www-data), but the mounted"
echo "!!!! directory belongs to someone else — usually a bind mount pointing"
echo "!!!! at a host directory owned by your own user."
echo "!!!! Give that directory to uid 82 on the host and start again:"
echo "!!!!"
echo "!!!! sudo chown -R 82:82 /path/to/your/storage"
echo "!!!!"
echo "!!!! See https://github.com/InvoiceShelf/docker/issues/77"
exit 1
fi
if [ ! -e /var/www/html/.env ]; then
cp .env.example .env
echo "**** Setup initial .env values ****" && \
@@ -33,9 +59,17 @@ if [ "$DB_CONNECTION" = "sqlite" ] || [ -z "$DB_CONNECTION" ]; then
chown www-data:www-data "$DB_DATABASE"
fi
echo "**** Setting up artisan permissions ****"
echo "**** Setting up folder permissions ****"
chmod +x artisan
# Only root may change ownership. The image normally runs as www-data, where
# this is both impossible and unnecessary — the files it created are already
# owned correctly — so it is skipped rather than failing the boot on a chown we
# are not permitted to make. It still helps anyone running the image as root.
if [ "$(id -u)" = "0" ]; then
chown -R www-data:www-data storage bootstrap/cache
fi
if ! grep -q "APP_KEY" /var/www/html/.env
then
echo "**** Creating empty APP_KEY variable ****"