Get Started
Features

Services

Auto-detect and manage deployable units within your project.

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?