Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions docs/content/docs/3.framework-guides/1.laravel/1.automations.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,61 @@ In order for this script to run,`AUTORUN_ENABLED` must be set to `true`. Once th
| `AUTORUN_LARAVEL_OPTIMIZE` | `true` | `php artisan optimize`: Optimizes the application. |
| `AUTORUN_LARAVEL_ROUTE_CACHE` | `true` | `php artisan route:cache`: Caches the routes. |
| `AUTORUN_LARAVEL_SKIP_IF_NOT_FOUND` | `false` | When `true`, the script will exit gracefully (without error) if Laravel is not detected in `APP_BASE_DIR`, instead of failing the container. Useful when `AUTORUN_ENABLED=true` is set on a shared image where Laravel may not always be present (e.g. before the first `composer install`). |
| `AUTORUN_LARAVEL_STORAGE_INIT` | `false` | Creates the `storage` and `bootstrap/cache` directory structure if missing and ensures they are writable by the owner and group. |
| `AUTORUN_LARAVEL_STORAGE_LINK` | `true` | `php artisan storage:link`: Creates a symbolic link from `public/storage` to `storage/app/public`. |
| `AUTORUN_LARAVEL_VIEW_CACHE` | `true` | `php artisan view:cache`: Caches the views. |

## Storage Initialization
Laravel expects a specific set of directories inside `storage/` and `bootstrap/cache` to exist and be writable. If they're missing, your application will fail to boot. Depending on which directory is missing, you'll see errors like these:

- `The /var/www/html/bootstrap/cache directory must be present and writable.`
- `View path not found.` (when running `php artisan optimize` or `php artisan view:cache`)

This commonly happens in containers when:
- Your `.dockerignore` excludes `storage/` or `bootstrap/cache` from the image build
- You mount an empty volume over `storage/`

Setting `AUTORUN_LARAVEL_STORAGE_INIT=true` runs this step first, before every other automation, so the rest of the Laravel automations can rely on these directories being in place.

::steps{level="3"}

### Create missing directories
The script creates any of these directories that don't already exist:

- `bootstrap/cache`
- `storage/app/private`
- `storage/app/public`
- `storage/framework/cache`
- `storage/framework/sessions`
- `storage/framework/testing`
- `storage/framework/views`
- `storage/logs`
- `storage/pail`

Directories that already exist are left alone, so this step is safe to run on every container start.

::warning
Failing to keep the [storage directory structure](https://laravel.com/framework/docs/master/structure#the-storage-directory) causes containerized Laravel processes to crash when trying to write logs or cache files.
::

### Make the directories writable
Each directory in the list above is made readable and writable by its owner and group. Only the directories themselves are changed. Files inside them, such as user uploads in `storage/app`, are never modified.

::note
Only the owner of a directory can change its permissions. If a directory is owned by another user (for example, `root`), the script logs a message, skips that directory, and continues starting the container.
::

### Using a volume for storage
The script runs as the unprivileged `www-data` user, so the directory you mount a volume on must be writable by `www-data`. When you create a new named volume, Docker copies the ownership of the matching directory in your image. If that directory doesn't exist in the image, Docker creates the volume owned by `root`, and the script won't be able to create anything inside it.

To avoid this, make sure `storage/` exists in your image.

::warning
If the script can't create a directory, the container will fail to start. Make sure the directory you mount on exists in your image and is owned by `www-data`.
::

::

## Database Connection Checks
Before running migrations, the automation script performs connection checks to ensure your database is ready. Understanding this process helps you configure timeouts and troubleshoot connection issues.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Setting environment variables all depends on what method you're using to run you
`AUTORUN_LARAVEL_MIGRATION_TIMEOUT`<br />*Default: "30"*|The number of seconds to wait for the database to come online before attempting `php artisan migrate`.. <br />ℹ️ Requires `AUTORUN_ENABLED = true` to run.| all
`AUTORUN_LARAVEL_ROUTE_CACHE`<br />*Default: "true"*|Automatically run "php artisan route:cache" on container start. <br />ℹ️ Requires `AUTORUN_ENABLED = true` to run.| all
`AUTORUN_LARAVEL_SKIP_IF_NOT_FOUND`<br />*Default: "false"*|When set to `true`, the Laravel Automations script will exit gracefully (without error) if Laravel is not detected in `APP_BASE_DIR`, instead of failing the container. Useful when `AUTORUN_ENABLED=true` is set on a shared image where Laravel may not always be present (e.g. before the first `composer install`). <br />ℹ️ Requires `AUTORUN_ENABLED = true` to have any effect.| all
`AUTORUN_LARAVEL_STORAGE_INIT`<br />*Default: "false"*|Automatically create the Laravel `storage` and `bootstrap/cache` directory structure and make them writable on container start. <br />ℹ️ Requires `AUTORUN_ENABLED = true` to run.| all
`AUTORUN_LARAVEL_STORAGE_LINK`<br />*Default: "true"*|Automatically run "php artisan storage:link" on container start. <br />ℹ️ Requires `AUTORUN_ENABLED = true` to run.| all
`AUTORUN_LARAVEL_VIEW_CACHE`<br />*Default: "true"*|Automatically run "php artisan view:cache" on container start. <br />ℹ️ Requires `AUTORUN_ENABLED = true` to run.| all
`CADDY_ACME_PROFILE`<br />*Default: "off"*|Select a Let's Encrypt ACME certificate profile. Valid options: `off` (default, no profile — keeps the stock Let's Encrypt + ZeroSSL issuers), `shortlived` (~6-day certs; also required for IP-address certificates), `tlsserver`, or `classic`. Setting any profile pins issuance to Let's Encrypt only and renews more frequently, so the container needs reliable egress to the ACME CA. (<a target="_blank" href="https://letsencrypt.org/docs/profiles/">Official docs</a>)|frankenphp
Expand Down
35 changes: 34 additions & 1 deletion src/common/etc/entrypoint.d/50-laravel-automations.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ script_name="laravel-automations"
: "${AUTORUN_DEBUG:=false}"
: "${AUTORUN_LARAVEL_SKIP_IF_NOT_FOUND:=false}"

# Set default values for storage link
# Set default values for storage
: "${AUTORUN_LARAVEL_STORAGE_INIT:=false}"
: "${AUTORUN_LARAVEL_STORAGE_LINK:=true}"

# Set default values for optimizations
Expand Down Expand Up @@ -165,6 +166,33 @@ artisan_storage_link() {
fi
}

laravel_storage_init() {
echo "🚀 Initializing Laravel storage structure and permissions in $APP_BASE_DIR"
for dir in \
bootstrap/cache \
storage/app/private \
storage/app/public \
storage/framework/cache \
storage/framework/sessions \
storage/framework/testing \
storage/framework/views \
storage/logs \
storage/pail; do
if [ ! -d "$APP_BASE_DIR/$dir" ] && ! mkdir_error=$(mkdir -p "$APP_BASE_DIR/$dir" 2>&1); then
echo "❌ $script_name: Unable to create directory: $APP_BASE_DIR/$dir"
debug_log "$mkdir_error"
return 1
fi

# Only the owner can change permissions, so directories owned by another user are skipped rather than failing startup
if ! chmod ug+rwx "$APP_BASE_DIR/$dir" 2>/dev/null; then
echo "ℹ️ Unable to update permissions on $APP_BASE_DIR/$dir, skipping"
fi
done

return 0
}

artisan_optimize() {
debug_log "Starting Laravel optimizations..."

Expand Down Expand Up @@ -443,6 +471,11 @@ wait_for_database_connection() {
############################################################################

if laravel_is_installed; then
# Runs first because Artisan fails to boot without bootstrap/cache
if [ "$AUTORUN_LARAVEL_STORAGE_INIT" = "true" ]; then
laravel_storage_init
fi

if [ "$LOG_OUTPUT_LEVEL" = "debug" ] || [ "$AUTORUN_DEBUG" = "true" ]; then
echo "Laravel detected: v$(get_laravel_version)"
echo "Automation settings:"
Expand Down