How to Deploy laravel with Inertia SSR

@fideloper-fly

I use this Dockerfile

# syntax = docker/dockerfile:experimental

# Default to PHP 8.2, but we attempt to match
# the PHP version from the user (wherever `flyctl launch` is run)
# Valid version values are PHP 7.4+
ARG PHP_VERSION=8.2
ARG NODE_VERSION=18
FROM fideloper/fly-laravel:${PHP_VERSION} as base

# PHP_VERSION needs to be repeated here
# See https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
ARG PHP_VERSION

LABEL fly_launch_runtime="laravel"

# copy application code, skipping files based on .dockerignore
COPY . /var/www/html

RUN composer install --optimize-autoloader --no-dev \
    && mkdir -p storage/logs \
    && php artisan optimize:clear \
    && chown -R www-data:www-data /var/www/html \
    && sed -i 's/protected \$proxies/protected \$proxies = "*"/g' app/Http/Middleware/TrustProxies.php \
    && echo "MAILTO=\"\"\n* * * * * www-data /usr/bin/php /var/www/html/artisan schedule:run" > /etc/cron.d/laravel \
    && cp .fly/entrypoint.sh /entrypoint \
    && chmod +x /entrypoint

# If we're using Octane...
RUN if grep -Fq "laravel/octane" /var/www/html/composer.json; then \
        rm -rf /etc/supervisor/conf.d/fpm.conf; \
        if grep -Fq "spiral/roadrunner" /var/www/html/composer.json; then \
            mv /etc/supervisor/octane-rr.conf /etc/supervisor/conf.d/octane-rr.conf; \
            if [ -f ./vendor/bin/rr ]; then ./vendor/bin/rr get-binary; fi; \
            rm -f .rr.yaml; \
        else \
            mv .fly/octane-swoole /etc/services.d/octane; \
            mv /etc/supervisor/octane-swoole.conf /etc/supervisor/conf.d/octane-swoole.conf; \
        fi; \
        rm /etc/nginx/sites-enabled/default; \
        ln -sf /etc/nginx/sites-available/default-octane /etc/nginx/sites-enabled/default; \
    fi

# Multi-stage build: Build static assets
# This allows us to not include Node within the final container
FROM node:${NODE_VERSION} as node_modules_go_brrr

RUN mkdir /app

RUN mkdir -p  /app
WORKDIR /app
COPY . .
COPY --from=base /var/www/html/vendor /app/vendor

# Use yarn or npm depending on what type of
# lock file we might find. Defaults to
# NPM if no lock file is found.
# Note: We run "production" for Mix and "build" for Vite
RUN if [ -f "vite.config.js" ]; then \
        ASSET_CMD="build"; \
    else \
        ASSET_CMD="production"; \
    fi; \
    if [ -f "yarn.lock" ]; then \
        yarn install --frozen-lockfile; \
        yarn $ASSET_CMD; \
    elif [ -f "pnpm-lock.yaml" ]; then \
        corepack enable && corepack prepare pnpm@latest-7 --activate; \
        pnpm install --frozen-lockfile; \
        pnpm run $ASSET_CMD; \
    elif [ -f "package-lock.json" ]; then \
        npm ci --no-audit; \
        npm run $ASSET_CMD; \
    else \
        npm install; \
        npm run $ASSET_CMD; \
    fi;

FROM base

COPY --from=node_modules_go_brrr /app/public /var/www/html/public-npm
# RUN npm install -g npm@9.6.7
# RUN npm install -g yarn@$YARN_VERSION --force

RUN rsync -ar /var/www/html/public-npm/ /var/www/html/public/ \
    && rm -rf /var/www/html/public-npm \
    && chown -R www-data:www-data /var/www/html/public

EXPOSE 8080

ENTRYPOINT ["/entrypoint"]

and Error this

2023-06-10T03:14:26.075 app[e784e5ea624483] sin [info] node: not found

2023-06-10T03:14:26.075 app[e784e5ea624483] sin [info] {"message":"node: not found\n","context":{"exception":{"class":"Inertia\\Ssr\\SsrException","message":"node: not found\n","code":0,"file":"/var/www/html/vendor/inertiajs/inertia-laravel/src/Commands/StartSsr.php:74"}},"level":400,"level_name":"ERROR","channel":"production","datetime":"2023-06-10T03:14:26.075489+00:00","extra":{}}

1 Like

Hi @bogordesain!

In order to fix the error above, you’ll have to include Node in the final image created by the Dockerfile.

Since we already use the node:${NODE_VERSION} image(w/c should contain node), the quickest change you can do is copy over the node available in that image to the final image. So right below the FROM base declaration, please add:

COPY --from=node_modules_go_brrr /usr/local/bin/node /usr/local/bin/node

Of course, please make sure to copy over its node_modules/ to the final image as well:

COPY --from=node_modules_go_brrr /app/node_modules /var/www/html/node_modules

Foot note:
The Dockerfile above contains two images, fideloper/fly-laravel and node:${NODE_VERSION}. But even so, only the final image declared will be used as the final image. Hence why we needed to copy over files from the node image to the fly-laravel image. You can read more on how multiple Dockerfile FROM declarations work here!

Also! You can run ssr as another vm to your Laravel Fly.io app by declaring it as a separate process in your fly.toml file( similar to running cron as a process ):

[processes]
  app=""
  ssr="php /var/www/html/artisan inertia:start-ssr"

Hi @bogordesain ! We’re working on some docs for this use case, thanks for the poke.

Let us know if this idea works for you (we’re testing it out soon to double check / document it for future people).

1 Like

this is good, but when i check using view source, ssr not load, just SPA only.

When i use @kathrynannetan solution this work for running machine. but on view source just show SPA.

in the docs of Inertiajs https://inertiajs.com/server-side-rendering command for Heroku like this :slight_smile:

web: php artisan inertia:start-ssr & vendor/bin/heroku-php-apache2 public/

But I don’t know with docker image,

maybe can be like this

web: php artisan octane:start & php artisan inertia:start-ssr 

?

maybe you can check this Laravel

Hi @bogordesain! I’m glad the solution worked! :fireworks:

One more step that needs to be done after setting up the ssr process, is to create communication between the “app” and “ssr” processes.

This can be done by adding a SSR_URL to the fly.toml’s [env] section, that points to the .internal address of the ssr process on port 13714:

[env]
  SSR_URL="ssr.process.<yourAppNameHerePlease>.internal:13714"

and using that env variable for the inertia.php config:

/* config/inertia.php */
'ssr' => [
    'enabled' => true,

    'url' => env('SSR_URL','http://127.0.0.1:13714')
],

Let us know if the above solution works, and if you have any additional questions as well!

1 Like

Also, thanks to your question @bogordesain, we now have this guide on Inertia SSR here!

Please check it out and let us know if anything else should be considered in the guide.

Happy flying on Fly.io!

2 Likes

Yes, It’s work. thanks bro

1 Like

Yeah… i agree for create guide. this awesome…

1 Like