Builds
Lucity builds container images from your source code using railpack. No Dockerfile required. Railpack detects your language and framework, then produces an optimized image. You focus on writing code; the platform handles the container plumbing.
Start a build
Trigger a build for a specific service with a single mutation:
mutation {
buildService(input: {
projectId: "myapp"
service: "api"
gitRef: "main"
}) {
id
phase
}
}
Optionally specify a gitRef (branch, tag, or commit SHA). If the service has a contextPath configured, the builder uses it automatically for monorepo builds. The mutation returns a build ID immediately. Builds are asynchronous. You don't wait around for the image to finish baking.
Track build progress
Poll the build status by ID to follow it through its phases:
query {
buildStatus(id: "build-abc123") {
id
phase
imageRef
digest
error
}
}
Build phases, in order:
| Phase | What's happening |
|---|---|
QUEUED | Waiting in line. Patience. |
CLONING | Fetching your source code from GitHub. |
BUILDING | Railpack is doing its thing: detecting, compiling, optimizing. |
PUSHING | Sending the finished image to the OCI registry. |
SUCCEEDED | Done. Image is ready. |
FAILED | Something went wrong. Check the error field. |
If a build fails, the error field gives you the details. No digging through obscure CI logs. The reason is right there on the build object.
Watch build logs
Real-time log streaming shows exactly what's happening during the build. Useful for debugging failed builds or just watching the progress for that oddly satisfying feeling of watching code compile.
Logs stream as they happen, so you're not waiting for the build to finish before you can see what went wrong.
Know where images go
Built images are pushed to the OCI registry (Zot in self-hosted setups). Each image is tagged with the git commit hash and labeled with metadata:
org.opencontainers.image.source: "https://github.com/acme/myapp"
org.opencontainers.image.revision: "a1b2c3d"
lucity.dev/built-by: "lucity-builder"
lucity.dev/service: "api"
The image tag is the git commit SHA. This means every image is traceable back to the exact code that produced it. No ambiguous latest tags, no guessing which commit is running.
Build from a monorepo
Working with a monorepo? Set the contextPath on the service when you add it:
mutation {
addService(input: {
projectId: "myapp"
name: "api"
port: 3000
sourceUrl: "https://github.com/acme/myapp"
contextPath: "services/api"
}) {
name
}
}
When you build or deploy the service, the builder automatically uses the configured context path. Each service in your monorepo can have its own build context, its own detected language, and its own resulting image. Railpack scans the specified directory independently, so your Go API and your Node.js frontend get the right build plans without stepping on each other.