This setup is designed for production deployment. For local development, use DevContainers or Laravel Sail.
Images are significantly smaller compared to Laravel Sail, each around 400MB for small to medium applications. Horizontally scalable can be achieved by deploying multiple PHP and SSR containers behind a load balancer. Use Redis for sessions/cache and shared storage (S3, NFS) for multi-instance deployments.
Obviously, we need a Laravel application. If you don't have one, you can install it using Podman:
# Install Laravel Installer
podman run --rm -it -v .:/app:z -v ~/.composer:/tmp:z docker.io/library/composer global require laravel/installer
# Create New Project
podman run --rm -it -v .:/app:z -v ~/.composer:/tmp:z --entrypoint /tmp/vendor/bin/laravel docker.io/library/composer new example-app
cd example-appNote: You must configure the Containerfile builder stages to run any artisan commands required for your build (e.g., wayfinder:generate, ziggy:generate, or nova:install).
This image runs PHP using FrankenPHP and serves the app with Caddy.
FROM docker.io/library/composer:latest AS composer_builder
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --ignore-platform-reqs --no-scripts --no-autoloader --prefer-dist
COPY . .
# Add build-time artisan commands here
RUN composer dump-autoload --optimize --classmap-authoritative \
&& php artisan wayfinder:generate --with-form
FROM docker.io/library/node:lts-alpine AS node_builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
COPY --from=composer_builder /app/resources /app/resources
RUN npm run build
FROM docker.io/dunglas/frankenphp:php8.5-alpine
ENV SUPERVISOR_PHP_COMMAND="/usr/local/bin/php -d variables_order=EGPCS /app/artisan octane:frankenphp --host=0.0.0.0 --admin-port=2019 --port=80"
RUN install-php-extensions \
bcmath pdo_mysql zip intl gd pcntl opcache \
&& apk add --no-cache supervisor \
&& rm -rf /tmp/* /var/cache/apk/*
COPY docker/runtime/start-container /usr/local/bin/start-container
COPY docker/runtime/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY docker/runtime/php.ini /usr/local/etc/php/conf.d/99-sail.ini
RUN chmod +x /usr/local/bin/start-container
WORKDIR /app
COPY --from=composer_builder /app/vendor /app/vendor
COPY --from=composer_builder /app/composer.json /app/composer.lock /app/
COPY --from=node_builder /app/public/build /app/public/build
COPY artisan ./
COPY app/ app/
COPY bootstrap/ bootstrap/
COPY config/ config/
COPY database/ database/
COPY routes/ routes/
COPY public/ public/
COPY storage/ storage/
COPY resources/views/ resources/views/
#https://github.com/containers/buildah/issues/5707
COPY lang* ./
EXPOSE 80 2019
ENTRYPOINT ["start-container"]Based on Laravel Sail entrypoint
#!/usr/bin/env ash
if [ ! -d /.composer ]; then mkdir /.composer; fi
chmod -R ugo+rw /.composer
if [ $# -gt 0 ]; then exec "$@"; else exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf; fi
Based on Laravel Sail supervisord.conf
[supervisord]
nodaemon=true
user=root
logfile=/tmp/supervisord.log
pidfile=/var/run/supervisord.pid
[program:php]
command=%(ENV_SUPERVISOR_PHP_COMMAND)s
user=root
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
You can also add worker process here. Refer to Laravel Queue docs.
Copied from Laravel Sail php.ini
[PHP]
post_max_size = 100M
upload_max_filesize = 100M
variables_order = EGPCS
pcov.directory = .
This image runs the SSR service using Node.js.
FROM docker.io/library/composer:latest AS composer_builder
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --ignore-platform-reqs --no-scripts --no-autoloader --prefer-dist
COPY . .
# Add build-time artisan commands here
RUN composer dump-autoload --optimize --classmap-authoritative \
&& php artisan wayfinder:generate --with-form
FROM docker.io/library/node:lts-alpine AS ssr_builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
COPY --from=composer_builder /app/resources /app/resources
RUN npm run build:ssr
FROM docker.io/library/node:lts-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --from=ssr_builder /app/bootstrap/ssr /app/bootstrap/ssr
COPY --from=ssr_builder /app/public /app/public
EXPOSE 13714
CMD ["node", "bootstrap/ssr/ssr.js"].git
.env
/vendor
/node_modules
/public/build
/bootstrap/cache/*
.phpunit.result.cache
/storage/*.key
Somehow buildah requires this folder to exist.
mkdir lang && touch lang/.gitkeep
'ssr' => [
'enabled' => (bool) env('INERTIA_SSR_ENABLED', true),
'url' => env('INERTIA_SSR_URL', 'http://127.0.0.1:13714'),
'ensure_bundle_exists' => (bool) env('INERTIA_SSR_ENSURE_BUNDLE_EXISTS', true),
],Adjust anything if needed. For example, I comment out the Wayfinder plugin because Node cannot find PHP; run commands separately.
// wayfinder({
// formVariants: true,
// }),INERTIA_SSR_URL=http://127.0.0.1:13714
INERTIA_SSR_ENSURE_BUNDLE_EXISTS=falsepodman build --file php.Containerfile -t localhost/example-app-php .
podman build --file ssr.Containerfile -t localhost/example-app-ssr .# Create Pod
podman pod create --name example-app -p 9999:80
# Start SSR
podman run -d --name node --replace --init --env-file .env --pod example-app localhost/example-app-ssr
# Start PHP
podman run -d --name php --replace --init --env-file .env --pod example-app localhost/example-app-phppodman network create example-app
# Start SSR
podman run -d --name node --replace --init --env-file .env --network example-app localhost/example-app-ssr
# Start PHP (Link to SSR container name)
podman run -d --name php --replace --init --env-file .env \
-e INERTIA_SSR_URL=http://node:13714 \
-p 9999:80 --network example-app \
localhost/example-app-phpFor SQLite, create a volume and mount it to /app/database in the PHP container. For a database container, deploy MySQL/PostgreSQL/MariaDB to the same pod or network. Use DB_HOST=127.0.0.1 for pods (shared localhost) or container name for networks.
If using local storage driver, create a volume and mount it to /app/storage in the PHP container to persist uploaded files and application data.