Published Sep 23, 2023
Last Updated Sep 23, 2023
Development With Laravel & Vite in Lando
Categories: Toolbox
Tags: #Lando , #Vite , #Laravel
By now, anyone who's started a new Laravel project knows Laravel has started using Vite out of the box instead of Laravel Mix, and Vite is freakin' awesome.
What's Vite?
Vite is a fast bundler and dev server package with built-in Hot Module Replacement support. Vite 3 was released mid 2020. I first saw it when the hype for v3 was being built.
What's Lando?
Lando is a Docker wrapper that helps create and orchestrate multiple containers for a project from a single tool. It also provides nice .lndo.site
dev URLs out of the box.
You can configure your own services as separate containers, or you can use provided recipes that should have what's needed out of the box, though the recipes seem geared towards PHP projects mostly right now.
Laravel support for Vite
It looked so good, it became Laravel's default bundler before Vite 3 stable was released! ?
Taking a quick shortcut
I don't normally do this because it kinda goes against my writing style. But this time, I'm going to put what I did right here, explain what the config options do, and then summarize how I arrived at these.
Tada, configs! ?
First, Vite:
import {defineConfig} from 'vite'
import laravel from 'laravel-vite-plugin'
import * as fs from "fs"
export default defineConfig({
server: {
hmr: {
host: '0.0.0.0',
},
host: '<my-project>.lndo.site',
https: {
cert: fs.readFileSync('/certs/cert.crt'),
key: fs.readFileSync('/certs/cert.key'),
},
port: 24690,
},
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true
}),
],
})
This is an overly simplified version of my config. The final config I actually built using environment vars, which made it messy to read.
Here the options I used:
server.hmr.host
This tells Vite what host the HMR client should look at. This is normally 'localhost'
. Since Lando is already routing traffic for the project hostname to the right container, I used that.
server.host
This tells Vite what host the HMR server should use for its websocket. This is also normally 'localhost'
, but that would restrict it to only traffic originating within the within the container itself. Using '0.0.0.0'
exposes the websocket to the virtual network managed by Lando.
server.https
- This is normally
false
. It can also betrue
or an object containing the container's own certificate and key. - Specifying the cert and key are necessary in this case, otherwise there will be a mismatch when trying to connect over HTTPS from the client.
server.port
This is the port opened for the websocket server-side.
Here's Laravel's default Vite config for comparison:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
],
});
Lando
Lando has what it calls a Landofile, named like Docker's Dockerfile—except not literally named that in the filename for some reason... The actual file is named ".lando.yml" (or ".lando.base.yml" in my case).
Here's my Landofile with some extra project-specific stuff removed:
name: <my-project> # placeholder
recipe: laravel
config:
php: '8.2'
composer_version: 2
webroot: public
database: mysql:8.0
services:
appserver:
build_as_root:
- curl -sL https://deb.nodesource.com/setup_18.x | bash -
- apt-get update -y
- apt-get install -y nodejs
build:
- yarn
ssl: true
ports:
- '24690:24690'
- '24691:24691'
- '24692:24692'
tooling:
node:
service: appserver
# if you want npm instead of yarn
# npm:
# service: appserver
yarn:
service: appserver
dev:
service: appserver
description: Start vite dev server
user: root
cmd: yarn dev
I'll include some extra links to help with the syntax. Also, you definitely don't
Here are the important parts for this article:
services.appserver.build_as_root
This is a list of commands to run to set up Node within the main project container "appserver".
- Install Node 18 (can be 20 if you want)
- Update apt's package list
- Install Node
services.appserver.build
I only run yarn
here, but you can also run npm install
. Lando is very nicely installs Yarn 1.22.x for you when you need Node.
Please do not do this step in build_as_root
. You'll be unpleasantly surprised when you find out you can't run lando yarn
or lando npm install
anymore; your permissions will be all messed up, forcing you to rebuild your appserver container.
Some background: before doing all of this directly in "appserver", I had tried creating a separate "node" service. Creating a separate Node service is fairly easy (I'll update if/when I write a post on that).
However, after much pain, I found out Vite's dev server had to be running within the same container as the rest of the project. This is because the certificate files for HTTPS need to match up. If you create a separate container, enable HTTPS and run Vite there, it'll get completely different cert files.
services.appserver.ssl
Setting this to true
is what tells Lando to enable HTTPS and generate certificate files.
services.appserver.ports
This gives Lando a list of additional ports to expose to the host machine. You can write these as individual ports, like this:
ports:
- 26790
- 26791
- 26792
... or as a list of 'external:internal' port mappings.
The reason I have a few ports is, if Vite sees a port (like 26790) is taken already, it'll try to use the next port up. And if it sees that one is taken, it'll try the next one after that, and the next one, and so on.
If that happens, you'll have to restart your container to try to free up the port(s) you need. Having a few reduces the risk that this will happen.
tooling
Each item listed here becomes a custom Lando command. For example, lando node
, lando npm
, lando npx
, lando yarn
...
In our case, we want lando dev
as well. This starts the Vite dev server as root within the container. This is necessary for it to have access to the certificate files. Lando builds these files as root.
If you want, you can have a run_as_root
step in your "appserver" service that does this. I did at first. But that makes it harder to stop/restart. This is what led to me having a separate command for this.
Wrapping up
There you go, now you know how to work with Laravel and Vite in a Lando development environment.
This ~~hair-ripping~~ pleasant side trip taught me a lot that I hadn't encountered before, including:
- Manually installing Ndoe and other services (like cron) inside of a container
- Explicitly allowing a Node server to accept traffic from outside localhost
- Needing to run a containerized Node service as root to allow it to use SSL certs
- Mapping separate sets of server and client hostnames and ports to play nice with a reverse proxy
One final note: early on, there was a bug in the Laravel Vite plugin preventing HMR options from getting passed down. This made me temporarily consider fully rebuilding the necessary Vite server config just to pass these options correctly. I'm very glad now that I didn't go down that rabbit hole. ?
Thanks for reading!
2 weeks ago
JSn1nj4 pushed to master at JSn1nj4/ElliotDerhay.com
2 weeks ago
JSn1nj4 created 2.10.1 at JSn1nj4/ElliotDerhay.com
2 weeks ago
JSn1nj4 pushed to develop at JSn1nj4/ElliotDerhay.com
1 month ago
JSn1nj4 pushed to feature/163-Auto-post-to-Mastodon at JSn1nj4/ElliotDerhay.com
1 month ago
JSn1nj4 closed Issue #199 at JSn1nj4/ElliotDerhay.com