This article walks through building a Laravel, Blade and MySQL development environment with Docker and Docker Compose, then compiling SCSS with Vite.
The short answer: you can either write the Dockerfile and compose file yourself, or use Laravel Sail. Write them yourself to understand the setup; use Sail to start working immediately.
Published in 2021 and revised in September 2026. Laravel Mix, used in the original, was replaced by Vite as Laravel’s default bundler in Laravel 9.2. The SCSS section now covers Vite, with a note for projects still on Mix.
The Docker configuration here is based on the environment shared by @ucan-lab; their write-up (in Japanese) goes into more detail on how it works.
Sponsored
Roll your own, or use Laravel Sail?
Laravel ships an official Docker environment called Sail. If you do not need to understand what is in the containers, Sail is dramatically faster to start with.
Sail is a command-line wrapper around Laravel’s default Docker setup. It consists of a compose.yaml file and a sail script, and brings up PHP, MySQL and Redis already wired together.
▼The two approaches compared
| This article (build it yourself) | Laravel Sail | |
|---|---|---|
| Files | You write the Dockerfile and compose file | compose.yaml is generated |
| Understanding | Everything is visible | Works without reading it |
| PHP version | You choose it | PHP 8.5 by default (8.0 to 8.5 available) |
| Adding services | Edit the YAML | sail:add |
| Suits | Wanting to know and adjust the setup | Starting development today |
Getting started with Sail is three commands:
composer require laravel/sail --dev
php artisan sail:install
./vendor/bin/sail up
The application is then at http://localhost, with MySQL, Redis and Mailpit running alongside it, and Node 24 installed in the container.
To avoid typing vendor/bin/sail every time, add a shell alias:
alias sail='sh $([ -f sail ] && echo sail || echo vendor/bin/sail)'
After that, sail up -d and sail artisan migrate work.
The rest of this article builds the environment by hand. Knowing how the containers fit together pays off when you move to production, or when you need something Sail does not anticipate.
Installing Docker Desktop for Mac
Download Docker Desktop from the official site and run the installer. Once it is running, check the version from the terminal:
docker -v
docker compose version
Sponsored
Building the Laravel environment with Docker
Define the application container, the web server and MySQL in a compose file, then create the Laravel project inside the container.
Create a project directory and a backend directory inside it for the Laravel application. The compose file describes three services: app for PHP, web for nginx and db for MySQL.
With the containers running, create the project:
docker compose exec app composer create-project --prefer-dist laravel/laravel .
Open http://127.0.0.1 and the Laravel welcome page confirms the environment works.
Displaying a view with Blade
Add a route and a Blade template. Blade is Laravel’s template engine, and files end in .blade.php.
Create resources/views/test.blade.php:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Hello, World</h1>
</body>
</html>
Register the route in routes/web.php:
Route::get('/test', function () {
return view('test');
});
Visit http://127.0.0.1/test and the heading appears.
Sponsored
Adding SCSS with Vite
Vite is Laravel’s asset bundler. It replaced Laravel Mix as the default in Laravel 9.2, and new projects ship with it already configured.
Vite pushes changed files straight to the browser without rebuilding everything, which makes it noticeably quicker to work with than Mix.
▼Laravel Mix and Vite compared
| Laravel Mix (old) | Vite (current) | |
|---|---|---|
| Config file | webpack.mix.js |
vite.config.js |
| Loading assets | The asset() helper |
The @vite() directive |
| Output | public/css, path of your choosing |
public/build, versioned automatically |
| During development | Rebuild each time | Only the change, applied instantly |
| Status | Still found in older projects | Default since Laravel 9.2 |
Installing Node.js and npm
Download Node.js from the official site and confirm the install:
node -v
npm -v
An old Node version will break the build. Fixing npm run errors by upgrading Node.js covers how to check and update it.
Installing the dependencies
cd backend
npm install
SCSS needs a compiler. If it is not already present:
npm install -D sass
Configuring vite.config.js
List your entry points in vite.config.js:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel([
'resources/sass/test.scss',
'resources/js/app.js',
]),
],
});
Unlike webpack.mix.js, there is no output path to specify. Vite writes hashed filenames into public/build and the loading side resolves them automatically.
The browserSync block Mix needed is also unnecessary — the @vite() directive detects the dev server and injects the reload mechanism itself.
Creating the SCSS file
Create resources/sass/test.scss:
h1 {
color: red;
}
Updating the Blade file
Load it with @vite(), not asset().
<head>
<meta charset="UTF-8">
<title>Document</title>
@vite(['resources/sass/test.scss'])
</head>
@vite() outputs a dev-server URL during development and a hashed production file after a build. You never write the path by hand.
Running the build
During development, leave the dev server running:
npm run dev
For production:
npm run build
With npm run dev running, saving the SCSS updates the browser immediately.
Checking it in the browser
Start the server in another terminal:
php artisan serve
Open http://localhost:8000/test. If the heading is red, the compilation worked.
If no styles apply, check that npm run dev is running. Vite does not write compiled files into public during development, so with the dev server stopped, @vite() points at nothing.
If the project still uses Laravel Mix
A project with a webpack.mix.js is running Mix. It still works, but development has moved to Vite, so migrate before adding much more to it.
Under Mix, the output path goes in webpack.mix.js and the Blade side loads it with asset():
<link rel="stylesheet" href="{{ asset('css/test.css') }}">
Summary
This covered building a Laravel, Blade and MySQL environment with Docker and Docker Compose, and compiling SCSS with Vite.
Build it by hand if you want to understand and adjust the setup; use Laravel Sail if you just want to start. Both are Docker underneath, so switching later is possible.
When working on an existing project, check which bundler it uses before touching the assets. A webpack.mix.js means Mix; a vite.config.js means Vite. Leaving both in place produces files in public with no clear origin.
To put the project on a server, deploying Laravel to Xserver with git picks up from here. If you need rich text editing, adding the Quill editor to Laravel fits into this environment directly.