Services
A service is a deployable unit within a project: a web server, an API, a worker, a background processor. Each service connects to its own source repository, so a project can pull from multiple GitHub repos. Most projects have at least one service. Monorepos might have several from the same repo.
Detect services automatically
Point Lucity at your repository and it figures out what's inside. The builder uses railpack to scan your source code and detect services with their language, framework, start command, and suggested port.
query {
detectServices(sourceUrl: "https://github.com/acme/myapp") {
name
language
framework
startCommand
suggestedPort
}
}
Railpack supports Node.js, Go, Python, Ruby, Rust, Java, PHP, Elixir, and many more. If it has a recognizable project structure, Lucity will find it. No Dockerfile required, though if you have one, that works too.
(Yes, it's a bit like magic. Except it's actually just good file heuristics and years of build-tool knowledge baked into railpack.)
Add a service
Once you know what you're deploying, add a service to your project:
mutation {
addService(input: {
projectId: "myapp"
name: "api"
port: 3000
sourceUrl: "https://github.com/acme/myapp"
framework: "express"
}) {
name
port
sourceUrl
framework
}
}
Provide a name, a port, and the source repository URL. The optional framework hint gives the dashboard a nice icon (purely cosmetic, but it makes the canvas look good). For monorepos, add a contextPath to build from a subdirectory.
Configure service settings
Each service has a handful of settings that matter:
- Port: the port your application listens on inside the container.
- Source URL: the GitHub repository containing the service's source code.
- Context Path: subdirectory for monorepo builds (e.g.
services/api). - Framework: a hint for the dashboard UI. Doesn't affect builds or deployments.
- Domains: public hostnames. Services with domains get HTTPRoute resources for ingress traffic. Services without domains are only reachable within the cluster.
These settings end up in the GitOps repo as Helm values. Change them through the API or dashboard and the packager commits the update.
View services on the canvas
The dashboard renders a visual service canvas for each project. Each service shows its framework icon, name, and per-environment status. You can see at a glance whether your API is healthy in production while your worker is still deploying in staging.
It's not just eye candy. The canvas is the fastest way to understand the state of your project across all environments.
Override the start command
Lucity auto-detects your start command from the source code, but sometimes you need a different entrypoint. Maybe you want to run database migrations before starting, or use a specific binary from a monorepo.
Set a custom start command per service:
mutation {
updateService(input: {
projectId: "myapp"
name: "api"
startCommand: "npm run start:production"
}) {
name
startCommand
}
}
The custom start command overrides whatever railpack detected. Remove it to fall back to automatic detection.
Scale your service
Services support both manual scaling and horizontal pod autoscaling (HPA).
Manual scaling sets a fixed replica count:
mutation {
updateServiceScaling(input: {
projectId: "myapp"
environmentId: "production"
service: "api"
replicas: 3
}) {
replicas
}
}
Autoscaling adjusts replicas based on CPU utilization:
mutation {
updateServiceScaling(input: {
projectId: "myapp"
environmentId: "production"
service: "api"
minReplicas: 2
maxReplicas: 10
targetCPU: 70
}) {
minReplicas
maxReplicas
targetCPU
}
}
Constraints: 1-20 replicas, 10-95% target CPU. Scaling is configured per environment, so you can run a single replica in development and autoscale in production.
Add custom domains
Every service gets a platform-generated domain automatically (e.g. api-production.lucity.app). For production, you'll want your own domain.
mutation {
addCustomDomain(input: {
projectId: "myapp"
environmentId: "production"
service: "api"
hostname: "api.example.com"
}) {
hostname
dnsVerified
}
}
After adding a custom domain, point your DNS to the provided target. Lucity verifies the DNS record and provisions a TLS certificate automatically. The domain maps to a Gateway API HTTPRoute, so it survives ejection as standard Kubernetes configuration.
Remove a service
Removing a service deletes its definition from the GitOps repo and cleans up the associated Kubernetes resources. The next ArgoCD sync removes the deployment, service, and any routes.
mutation {
removeService(projectId: "myapp", service: "api")
}
Your source code is unaffected. Lucity doesn't touch your repo, remember?