Moving my Astro blog to Laravel Cloud
Laravel Cloud does not officially support Astro, but Astro can run as a standalone Node server. I moved this blog from Fly.io to find out whether that was enough.
Why move a working site?
Fly.io has always worked well for me. I never had a problem with the blog there, and this move was not an attempt to fix one.
I plan to use Laravel Cloud for other projects, though, and I would rather not spread deployments across more services than necessary. When Cloud added support for Nuxt and Next.js applications, I started wondering whether it could also run Astro. Cloud’s documentation lists those two frameworks for its JavaScript runtime rather than advertising support for generic Node applications, so Astro was still an undocumented deployment path. If it worked, Cloud could become a home not only for my Laravel applications but also for the sites around them.
This blog was a safe place to test the idea. It has little traffic, nothing critical depends on it, and its deployment was already simple.
The static deployment that did not work
My first attempt used the blog’s existing statically generated output. Laravel Cloud did not recognise the resulting folder structure as something it could deploy. I mostly expected that: Astro was not officially supported, and Cloud appeared to expect an application process it could start and check.
The next step was to make the site look like an ordinary Node application. I added Astro’s Node adapter:
pnpm astro add node
Then I configured it in standalone mode:
import node from "@astrojs/node";
import { defineConfig } from "astro/config";
export default defineConfig({
adapter: node({
mode: "standalone",
}),
});
Standalone mode produces a server entry point that also serves the built assets. The start command in Laravel Cloud is therefore just:
node ./dist/server/entry.mjs
Cloud correctly detected that the repository uses pnpm and added the pnpm setup and build steps automatically. It did not detect the start command, so I configured that myself.
A server that Cloud could not reach
The Node version built and started, and its logs said it was listening on port 3000. The deployment still failed because Laravel Cloud could not complete its health check.
With no Astro-specific deployment guide to consult, I deployed a Nuxt application, which Cloud officially supports, and compared its logs. The useful difference was easy to miss: Nuxt was binding to ::, while Astro was binding to 0.0.0.0 by default.
I changed the application environment variables in Cloud to:
HOST=::
PORT=3000
:: binds the server to the IPv6 wildcard address that Cloud’s health check could reach. The next deployment worked immediately.
That was the entire workaround. It took about a day to get from the first static deployment to a working application, with most of the uncertainty coming from not knowing whether Astro could run there at all.
A small runtime tradeoff
The blog now runs as a Node process instead of being served as a purely static deployment. That is more runtime machinery than the site strictly needs, but Laravel Cloud’s scale-to-zero behaviour makes the tradeoff reasonable for me.
I expect Laravel Cloud to eventually support statically built Astro sites, which would remove that tradeoff. I have already sent the team feedback asking for it.
A personal blog with modest traffic can remain off most of the time and wake when a request arrives. There is a slight cold-start delay, but not enough to matter for this site.
One place for more of the stack
The point of this move is not that Laravel Cloud is a better blog host than Fly.io. It is that I can use Cloud for a Laravel application and the sites around it.
I would still run an Astro marketing site as a separate Cloud application. With Cloud’s recent monorepo support, however, the marketing site and the Laravel application can live in the same Git repository while retaining independent deployments.
For me, that means one less platform to maintain. This blog proved that the setup works: build Astro with the standalone Node adapter, tell Cloud how to start it, and bind the server to ::.