Build Pipeline
Builds run on Lizard’s build nodes — you don’t need Docker locally. When you deploy, the platform decides how to turn your source into a container image using a fixed decision order, then runs it in a Firecracker micro-VM.
Build decision order
The platform picks exactly one build strategy, in this order:
- Synthesized Dockerfile — if
buildCommandand/orstartCommandare set on the service (or passed vializard up --build-command/--start-command), Lizard generates a Dockerfile from those commands. lizardpack is not invoked. - Repo Dockerfile (verbatim) — if
dockerfilePathis set on the service, that Dockerfile from your repo is used unchanged. - lizardpack auto-detect — otherwise the platform clones your source and runs lizardpack, its buildpack/Dockerfile generator.
lizardpack auto-detect
lizardpack inspects your repo and builds an optimized multi-stage image. Supported stacks, matched in this order:
Go → Node → Python → Rust → Ruby → PHP → Java → static
On this path:
- If a repo
Dockerfileexists and has a real build step (aRUN <package-manager>line, not justCOPY dist/), it’s used verbatim. - Otherwise lizardpack generates the Dockerfile for you.
- The start command is auto-detected: a
Procfileweb:line (Python/Ruby) orpackage.jsonscripts.start(Node) is picked up automatically. - The port is inferred from
EXPOSE, framework defaults, or an explicitPORTenv.
Gotcha: A Dockerfile that only copies pre-built artifacts (
COPY dist/,build/,out/,.next/,public/) without aRUNbuild step is treated as incomplete and gets regenerated by lizardpack. Add a real build step, or setdockerfilePathto force verbatim use.
What triggers a rebuild
| Action | Rebuilds? |
|---|---|
git push to the tracked branch | ✅ via GitHub webhook |
lizard redeploy / lizard up | ✅ explicit |
Changing VITE_* or NEXT_PUBLIC_* vars | ✅ build-time values are baked in |
service set of build fields (repoUrl, branch, sourceType, buildCommand, dockerfilePath, rootDirectory) | ✅ auto-rebuilds |
service set of runtime-only fields (startCommand, preDeployCommand, containerPort, watchPatterns) | ❌ run lizard redeploy to apply |
| Any other env var / secret change | ❌ pushed live to the running VM (no rebuild) |
Don’t double-build. After a
service setthat changes a build field, the rebuild fires automatically — don’t chain alizard redeployafter it, or you’ll queue a second redundant build.
Watching a build
Build logs stream during lizard up. For any service:
lizard logs --build # the most recent build's logs
lizard events # deploy history + replica statusIf a build fails, read lizard logs --build, fix the cause (in your repo, or by adjusting buildCommand / startCommand via lizard service set), then lizard redeploy.
Runtime notes
- No Docker
HEALTHCHECK. Firecracker VMs don’t run Docker’s healthcheck loop, soHEALTHCHECKdirectives are ignored. Lizard checks port reachability instead (skipped in worker mode). - Don’t write a Dockerfile unsolicited. lizardpack auto-detects most stacks — try a deploy first, and only add a Dockerfile (or set
dockerfilePath) if the auto-build doesn’t fit.
Build configuration reference
| Field | Effect |
|---|---|
buildCommand | Command to build your app → forces the synthesized Dockerfile path |
startCommand | Command to start your app at runtime |
preDeployCommand | Runs once before each deploy (e.g. DB migrations) |
dockerfilePath | Path to a repo Dockerfile to use verbatim |
rootDirectory | Subdirectory to build from (monorepos) |
watchPatterns | Only redeploy when matching paths change |
containerPort | TCP port your app listens on (default 3000; 0 = worker) |
Set any of these with lizard service set <svc> --set <field>=<value>. See the CLI Reference.