# Set up a service

A service is one thing you deploy inside an environment: a source, the settings it runs with, its own variables, its own domains and its own release history. Creating one gives it a generated `lessly.run` address immediately, routed to it and usable straight away — see [Connect a domain](/ship/deployment/domains).

Every service is one of two types. A **runtime service** is a container Lessly keeps running for you. A **static site** is a set of files served as they are, with nothing running between requests.

## Pick a type and a source

Each type accepts its own set of sources.

| Type | Git repository | Prebuilt image | Uploaded archive |
|---|---|---|---|
| **Runtime service** | yes | yes | no |
| **Static site** | yes | no | yes |

The type also decides which settings exist at all.

| | Runtime service | Static site |
|---|---|---|
| Port | yes | no |
| Replicas, CPU and memory | yes | no |
| Health checks | yes | no |
| Scale to zero | yes | no |
| Container logs and the terminal | yes | no |
| HTTP metrics | yes | yes, counted per hostname |
| Variables | yes, at run time | non-secret ones, at build time only |
| Custom domains and routes | yes | yes |

The split is not a matter of which settings are offered: publishing a static site skips the container pipeline outright — nothing is scheduled, nothing waits to report healthy — so there is no container for a port, a replica count, a health check or a shell to apply to. What a static site does not lose is visibility into its traffic: requests, error rate and latency are measured for it at the edge and counted per hostname. What it has none of is the output of a running process. See [Read logs, events and metrics](/ship/deployment/observability).

The two types are told apart by the source you give the service when you create it, and changing a service from one type to the other means giving it a source of the other type.

## Where each action lives

| Surface | Service actions |
| --- | --- |
| Product App | **Add service** on the environment canvas creates one; the service becomes a node, and clicking it opens its panel. The name and the source are on **Service → Overview**, everything else on **Service → Settings**. |
| CLI | Every action on this page. The CLI renders the same operation catalog as MCP — anything an agent can call, it can run. [Sign in from the terminal](/interfaces/cli). |
| MCP | Every action on this page. Tool names are linked per section. |
| REST | Every action on this page, under `/deployment/services/…`. [Browse the endpoints](/reference/openapi/deployment). |

## Create the service

1. **Name it and give it a source.** [`deployment_service_create`](/reference/mcp-tools/deployment_service_create) takes the environment, a service name of up to 80 characters, and the source — which is also what fixes the type.
2. **Read the slug back.** It is derived from the name and unique within the environment, and it is the name other services use to reach this one over the private network. Two environments may each have a service called `api`; within one environment the name is taken.
3. **Change any field later** with [`deployment_service_update`](/reference/mcp-tools/deployment_service_update).

## Runtime services

A runtime service runs a container image. Its source says where that image comes from — either a repository Lessly builds, or an image you already publish.

### From a repository

Point the service at a repository and a branch, and optionally at a subdirectory inside it for a monorepo. The repository may be public or private; a private one is read through the GitHub connection your product already has.

The build is driven by a `Dockerfile` — there is no automatic framework detection.

- The `Dockerfile` is expected at the root of the repository, or at the subdirectory you set as the service path.
- The build context is always the repository root, so `COPY` paths are written relative to the repository root rather than to the directory the `Dockerfile` sits in.
- If no `Dockerfile` is found, the deploy fails before any build time is spent, and the error says exactly where it looked.

Pushes to the tracked branch redeploy the service automatically. A service currently at zero replicas is a case of its own — a push brings some of them back and is ignored by others; see [Scale a service](/ship/deployment/scaling). The commit that was built is recorded on the deployment and is available to the running container as `LESSLY_GIT_COMMIT_SHA`, alongside `LESSLY_GIT_BRANCH`.

### From a prebuilt image

Give an image reference — a registry and repository, plus a tag. There is nothing to build: the image is pulled and run as it is. This is the fastest kind of deploy, and the right one when your image is produced by a pipeline you already own.

### Settings

| Setting | Default | What it does |
|---|---|---|
| **Port** | 8080 | The port your container listens on. The value is also passed into the container as `PORT`, so an application that reads `PORT` needs no configuration at all. |
| **Replicas** | 1 | How many copies of the container run, from 0 to 50. Setting it to 0 stops the service without deleting anything; setting it back starts it again. Replicas can be changed on their own, without a new deploy — see [Scale a service](/ship/deployment/scaling), which also covers CPU and memory. |
| **Scale to zero** | off | Opt-in. Lets an idle service stand down instead of running around the clock, and wake again on the next request. See [Scale a service](/ship/deployment/scaling). |
| **Readiness check** | none | An HTTP path — starting with `/` — with an optional port, which defaults to the service port, and optional timings: initial delay, period, timeout, and how many consecutive results count as a change of state. Decides when a new release is considered up and may take traffic. |
| **Liveness check** | none | Same shape as the readiness check. Decides when a running copy is unhealthy and should be replaced. |

Define neither check and none is applied: a release counts as up once it starts. Everything the service reads from its environment, including secrets and references to a managed database, arrives as a variable — see [Set variables and secrets](/ship/deployment/variables).

## Static sites

A static site is HTML, CSS and JavaScript with no server of its own. It takes either a repository or an uploaded archive; a prebuilt container image is not one of its sources, because there is no container.

| Source | What Lessly does |
|---|---|
| **Built from a repository** | A repository, a branch and an optional subdirectory, as for a runtime service. The build runs `npm ci && npm run build` and publishes the `dist` directory unless you set a different build command or output directory. Non-secret variables are available to the build. Pushes to the branch always rebuild and republish. |
| **Uploaded prebuilt** | You upload an archive of an already-built site and deploy that. No build runs. |

Two switches change how the site is served. Both take effect without a redeploy.

| Switch | On | Off |
|---|---|---|
| **Single-page application** | A request for a path that does not match a file falls back to the root `index.html` — what a client-side router needs. | `/privacy` resolves to `privacy/index.html`, and a genuinely missing path returns your own `404.html` with a real 404. |
| **Markdown for agents** (on by default) | A client that asks for Markdown gets a Markdown rendering of the page instead of HTML — what coding agents and other tools reading your documentation want. | Every client gets HTML. |

A site can also ship a `_headers` file in its build output, in the same grammar Cloudflare Pages and Netlify use, to set custom response headers per path.

Publishing a static deploy is atomic: a visitor gets either the previous version or the new one in full, never a mixture of the two.

## Change, restart or delete

You can change the name and the source of any service, and — on a runtime service — the port, the replica count, the health checks and the resource limits.

Configuration changes are recorded immediately and are picked up by the next deploy, so after editing a source or a port, [redeploy the service](/ship/deployment/deploy) to put the change into effect. Three changes apply immediately instead: the replica count and the two static-site switches.

**Restart** with [`deployment_service_restart`](/reference/mcp-tools/deployment_service_restart) — under Operations on **Service → Settings** — rolls a runtime service over onto the same image, without building or creating a new release. It is the right tool when a process has gone bad but the release itself is fine.

> **WARNING**
> Deleting a service with [`deployment_service_delete`](/reference/mcp-tools/deployment_service_delete) — the **Remove** button on the service panel — stops it, releases its generated domain and removes its release history. Volumes are not deleted with it: they are separate resources and have to be [removed explicitly](/ship/deployment/data).

## Next steps

- [Deploy, redeploy and roll back](/ship/deployment/deploy): what starts a deploy, and how to go back a release.
- [Scale a service](/ship/deployment/scaling): replicas, CPU and memory, and sleeping when idle.
- [Connect a domain](/ship/deployment/domains): put a domain you own in front of the service.
- [Work with environments](/ship/deployment/environments): run the same service again in `staging` or in a pull-request preview.
- [Read how deployment works](/ship/deployment): the product, environment and service model these settings sit in.
