Preview environments for a decoupled Laravel + Nuxt stack
Two services, one environment. Every pull request boots your Laravel API and a separate Nuxt 3 SSR frontend on the same managed VPS — migrated, seeded, and wired to each other with CORS, Sanctum cookies and an auto-injected API base URL. One HTTPS link, torn down clean when the PR closes.
- ✓ Nitro SSR or static
- ✓ Sanctum + CORS wired
- ✓ Seeded MySQL / Postgres
An API and a frontend, provisioned together and wired
A decoupled stack is two deployments, not one. We bring up both inside a single environment and connect them, so the frontend you review is always pointed at the matching API.
Laravel API
- ✓
composer install, real web server - ✓
migrate --seed - ✓ Isolated MySQL / Postgres
- ✓ Redis,
queue:work+ scheduler
Nuxt 3 frontend
- ✓
npm cithennuxt build - ✓ Nitro Node SSR process
- ✓
runtimeConfig.publicAPI base - ✓ Stateless — talks HTTP to the API
Two deploys that have to agree about each other
A single-origin app is one box. A decoupled Laravel + Nuxt stack is two services that must line up on URLs, CORS allow-lists and cookie domains — and every one of those is easy to get subtly wrong by hand, per branch.
The API base goes stale
The Nuxt build bakes in an API URL that points at someone’s laptop or last week’s box, so the reviewed frontend talks to the wrong backend.
CORS and cookies fight you
Cross-origin requests get blocked, Sanctum cookies don’t stick because SESSION_DOMAIN is off, and login silently fails only in the preview.
Two deploys, one chance to drift
You stand the API up, then the frontend, then patch env by hand. Miss a variable and SSR renders one thing while the browser fetches another.
What each Laravel + Nuxt preview includes
Both services and their backing infrastructure boot on the same VPS, connected and served behind one HTTPS hostname.
composer install, served by a real web server.migrate + seeders on boot.queue:work worker and the scheduler running.nuxt build then a long-lived Node process — real server-side rendering.NUXT_PUBLIC_API_BASE set to this environment’s API URL before build.Two services, declared once and run on every push
You give each service its install and build commands. We run them in clean containers, bring the API up first, then build the Nuxt app against its live URL.
# service A — laravel api (brought up first) composer install --optimize-autoloader php artisan migrate --seed # isolated DB, realistic data php artisan queue:work & php artisan schedule:work ✓ api https://api.acme-feed.admn.cloud # service B — nuxt 3 frontend, wired to the api above export NUXT_PUBLIC_API_BASE=https://api.acme-feed.admn.cloud npm ci && nuxt build # nitro node server output node .output/server/index.mjs # long-lived SSR process ✓ web https://acme-feed.admn.cloud
From two branches to one shareable URL
- 01
Define both services
Point ADMN at your API and Nuxt app — one monorepo or two repos — and declare each service’s build commands and database template once.
- 02
Pick the branches
Choose a branch (or a PR of each). We provision the VPS, bring the API up with a seeded database, then build Nuxt against its live API URL.
- 03
Share one URL
A real HTTPS frontend talking to its matching API — CORS and Sanctum already working. Renew the lease to keep it, or let it expire and tear down clean.
Decoupled Laravel + Nuxt previews, answered
How do the two services find each other?
We provision both services inside the same environment and wire them automatically. The Laravel API comes up on its own in-environment HTTPS URL, and that URL is injected into the Nuxt service as NUXT_PUBLIC_API_BASE before `nuxt build` runs. You never hard-code an API host or paste URLs between deploys — the frontend always points at the matching API for that exact branch.
Does Sanctum SPA cookie authentication work across the two origins?
Yes. Because the API and frontend live in the same environment, we configure Sanctum and CORS for you: SANCTUM_STATEFUL_DOMAINS and SESSION_DOMAIN cover the frontend origin, the CORS allow-list permits it with credentials, and SESSION_DOMAIN is set so the auth cookie is shared. The `/sanctum/csrf-cookie` handshake and credentialed XHR/fetch calls work exactly like production.
Does Nuxt run as a real SSR Node server, or static?
Both are supported. By default we run `nuxt build` and start the Nitro Node server as a long-lived process, so server-side rendering, server routes and runtimeConfig all behave like production. If your app is `nuxt generate` (static / SPA), we serve the prerendered output instead — you pick per project.
One repo or two?
Either. A monorepo with the API and Nuxt app in separate directories works — you point each service at its subdirectory and declare its build commands. Two completely separate repositories also work: a preview environment can pin a branch of each, so a coordinated API + frontend change previews together.
Where does the database live?
With the API. Each environment gets its own isolated MySQL or PostgreSQL database attached to the Laravel service, migrated with `php artisan migrate` and seeded on first boot. Redis is provisioned alongside it for cache, sessions and queues. The Nuxt service is stateless — it only talks to the API over HTTP.
What happens to runtimeConfig and client-side API calls?
NUXT_PUBLIC_API_BASE is a public runtime key, so it is available both during SSR (server-side fetches from the Nitro process) and in the browser bundle (client-side calls after hydration). We set it once at build time to the environment’s API URL, so server and client agree on where the API is — no env drift between the two halves of a render.
Preview your API and Nuxt frontend together
Connect GitHub and get both services provisioned, wired and seeded for every branch — one HTTPS URL, destroyed without a trace.
