LILYLILYDOCS
Docs/Guides/Deploy Next.js

Deploy a Next.js site

From npx create-next-app to a live URL in twenty lines of shell.

This guide takes a fresh Next.js App Router project and puts it on the public LILY fleet. No Dockerfile. No CI config. No platform settings to click through. The whole loop is one CLI plus your editor.

You need lly installed and authenticated. If you have not run install and lly login yet, do that first.

1. Scaffold

Use the official Next.js scaffolder. Pick the App Router, TypeScript, Tailwind — whatever you want. LILY treats the project as-is.

scaffold
$ npx create-next-app@latest dr-demo √ Would you like to use TypeScript? … Yes √ Would you like to use App Router? … Yes √ Would you like to use Tailwind CSS? … Yes Creating a new Next.js app in /home/you/dr-demo. ✓ Success. Created dr-demo at /home/you/dr-demo $ cd dr-demo

2. Install the plugin

The Next.js frontend ships as a plugin, not as part of the core CLI. Install it once per machine. It registers under lly nextjs.

plugin install
$ lly plugin install nextjs → resolving nextjs from rc.lilylabs.io → nextjs@0.7.3 · x86_64-linux · 12.4 MB ✓ installed to ~/.lly/plugins/nextjs/ ✓ verified signature (sha256:7c2f…d901)

Confirm the install with lly plugin list or inspect the full surface at lly nextjs.

3. Build the void

A void packs your whole Next.js project — server, routes, static assets — into one brick. The brick is a self-contained WASM bundle that runs anywhere the LILY runtime runs. There is no separate Node process, no next start, no sidecar.

void build
$ lly nextjs void → discovering routes → 4 routes · 2 static · 2 dynamic → compiling app/ to wasm (machineroom 0.31.0) → bundling hydration islands (esbuild) → packing static assets · public/ 142 KB ✓ wrote ./void.brick (3.8 MB) in 6.4s

The output is a single file, ./void.brick. You can inspect it with lly bundle inspect ./void.brick to see routes, asset map, and module list.

4. Deploy

lly void manages placements on the public fleet. One command uploads the brick, registers ownership against your signed-in account, and asks Raptor to schedule it onto a CAP. You get a live URL back.

deploy
$ lly void deploy ./void.brick → uploading 3.8 MB to platform.lilylabs.io → placement requested · pool=prod → scheduled on cap-7 (europe-west4-a) → health check OK in 1.2s ✓ live at https://lazy-fox.app.lilylabs.io

The slug — here lazy-fox — is the public handle for this placement. Use it for logs, kills, and inspection. The HTTPS certificate is provisioned automatically.

5. Iterate

The deploy loop is the same three commands every time. Edit a page, rebuild the void, re-deploy. Existing placements with the same slug are replaced atomically with under a second of drain.

re-deploy
$ $EDITOR app/page.tsx $ lly nextjs void ✓ wrote ./void.brick (3.8 MB) in 2.1s $ lly void deploy ./void.brick → replacing lazy-fox · drain 0.4s ✓ live at https://lazy-fox.app.lilylabs.io

The second build is faster because the route graph and the hydration bundle are content-addressed in ~/.cache/lly/. Unchanged chunks are reused.

6. Logs

Tail stdout and stderr from the running placement. Logs stream through Raptor over a persistent connection.

logs
$ lly void logs lazy-fox 2026-06-04T10:14:02Z GET / 200 18ms 2026-06-04T10:14:02Z GET /about 200 9ms 2026-06-04T10:14:11Z GET /api/now 200 3ms 2026-06-04T10:14:18Z GET /unknown 404 1ms

Add --follow to stream continuously. Add --since 1hfor a window. See lly void for the full set of log flags.

7. Tear down

When you are done, kill the placement. The brick is unloaded from the CAP, the slug is released back to the pool, and the DNS entry is retired within a few seconds.

kill
$ lly void kill lazy-fox → draining lazy-fox ✓ placement removed

That is the whole loop. Scaffold, install the plugin, build the void, deploy, iterate, log, kill. For the per-command reference see lly nextjs and lly void.