Deployment
Watch a service
Find out whether a service is healthy — and be told when it stops being — from logs, events, metrics and alert rules.
Lessly keeps three different records of a running service: what your own code said, what Lessly did to the service, and what the service consumed and answered. Start from the symptom and the record follows.
Where each action lives:
| Surface | Logs, events, metrics and alerts |
|---|---|
| Product App | Environment → Logs for the merged feed, Service → Logs for the live tail on one service, and a build log from the deployment that produced it under Service → Deployments. Environment → Metrics for the charts; Environment → Alerts for the rules, or straight from a target on the environment canvas. |
| CLI | Every action on this page: the CLI renders the same operation catalog as MCP. Run it from your own terminal. |
| MCP | Every action on this page. Tool names are linked per section. |
| REST | Every action on this page, under /deployment/…. Browse the endpoints. |
Choose the record
| You want to know | Look at | Where |
|---|---|---|
| What my application said, and when | Logs — every line a container printed to stdout or stderr, plus build output and managed database logs | The environment log feed, or the live tail on one service |
| What happened to my service — a deploy, a failed health check, a restarted replica | Events — Lessly’s own record of what it did | The service, a single deployment, or the whole environment |
| Whether it is slow, heavy or failing | Metrics — CPU, memory, network, request rate, latency, error rate | The environment’s metrics page |
| Without watching a page | Alert rules — a threshold on one metric on one target | The environment’s alerts page |
Alerts are the one record that finds you rather than the other way round — with one limit worth reading before you rely on them, in Set an alert rule.
Read the logs
- Open the environment log page. It merges every source in the environment into a single stream, newest first. This is the place to look when you do not yet know which service is at fault: an error in one service and the database line that caused it appear next to each other, in the same feed.
- Narrow the window: the last 5 minutes, 15 minutes, 1 hour or 24 hours.
- Filter. Level chips turn
error,warn,infoanddebugon and off; source chips give one per service, managed service and database present in the window, each with its own colour; each chip carries its own count. Search is free text, matched against both the message and the structured fields of the line, so searching for an id finds it wherever it sits. - Turn on live tail to have new lines arrive as they are printed and merge into the top of the feed. With it off you are reading history, and the feed pages backwards on demand within the chosen range.
- Select a line to open a detail panel with its exact timestamp, source, channel and level, the full message, and — for structured lines — every field parsed out. From there you can copy the line, copy the timestamp, or, if the line carries a
request_id, filter the whole feed down to that one request.
A histogram above the feed splits the range into sixty buckets and stacks the levels in each, so a burst of errors is visible before you read a single line. It follows the search box, not the level and source chips: it shows what the window contains, including the levels you have currently hidden.
Through the Lessly MCP server: deployment_service_logs for one service, deployment_build_logs for a build, deployment_managed_service_logs for a database. Token scope: a key with read access to the environment.
Where each kind of log lives
| Log | What it is | Note |
|---|---|---|
| Environment logs | Every source in the environment, merged, newest first. | The place to start when you do not know which service is at fault. |
| Service runtime logs | A live tail on the service, showing only what it is printing right now. | The fastest way to watch a single service while you exercise it. |
| Build logs | The output of a build, attached to the deployment that produced it. | Streams while the build runs and stays readable afterwards. A deployment whose source is a prebuilt image has none — nothing was built. |
| Static sites | No runtime logs at all — there is no long-running container to print anything. | Their record is the build and deploy output on the deployment. |
Three kinds of source end up in the environment feed:
| Source | What it is | Live-tailed |
|---|---|---|
| Services | The stdout and stderr of your containers. Every replica of a service writes into the same stream. | Yes |
| Managed services | The output of a managed engine Lessly runs inside the environment — PostgreSQL, MySQL, MongoDB or Redis — attributed and labelled by its own name, exactly as one of your services would be. | Yes |
| Managed databases | The database’s own log, labelled with the instance it came from. | No. The lines are shown from history and on refresh, and the page says so when such a database is present in the window. |
Only the currently serving release and the newest release are included. Output from older, already-replaced releases is left out, so a busy service does not bury today’s lines under yesterday’s. A release that is crashing and never becomes the serving one is still shown — that is exactly the output you need.
How a line gets its level
Every line gets one of four levels: error, warn, info, debug.
| The line | Where the level comes from |
|---|---|
Structured JSON with a level field | The level field, in either the textual (error, warn, info, debug, fatal, trace, …) or numeric form. |
| Anything else | The text. A line mentioning fatal, error or err is treated as an error, warn as a warning, debug or trace as debug. |
| Anything Lessly cannot classify | info. |
The same rule is applied to the live tail and to history, so a line never changes level depending on where you look at it.
Read the events
Events are recorded by Lessly itself as it works, and are kept per service, per deployment and per environment. Each carries a timestamp, a type, and the details belonging to that type. List them for a service — optionally narrowed to a single deployment — or for a whole environment, filtered by type and by time: deployment_events_list_by_service, deployment_events_list_by_environment.
A deploy produces a chain of them:
| Event | What it means |
|---|---|
deployment.queued | The deploy was accepted and is waiting to start |
deployment.build.started | The build began |
deployment.build.succeeded | The build finished and produced an image |
deployment.build.failed | The build failed; no new release is started |
deployment.deploying | The new release is being started |
deployment.variable.ignored | A variable you set collided with one of the six system variables Lessly owns and was discarded; the deploy continues with Lessly’s value. It is recorded only for those six — see Set variables and secrets |
deployment.healthcheck.passed | The new release answered its health check |
deployment.healthcheck.failed | The health check timed out or answered with an error |
deployment.healthy | The release is up and is now taking traffic |
deployment.failed | The deploy did not produce a serving release |
deployment.replaced | This release stopped serving because a newer one took over |
deployment.release.started | The release command began |
deployment.release.log | A batch of output lines from the release command |
deployment.release.succeeded | The release command finished successfully |
deployment.release.failed | The release command failed, and the deploy fails with it |
The rest of the service’s life is recorded too:
| Event | What it means |
|---|---|
replica.restarted | A replica was restarted after it exited |
service.scaled | The replica count changed |
service.restarted | A restart was requested for the service |
service.restart-failed | The restart could not be applied |
service.sleep-enabled | Sleeping when idle was turned on for the service |
service.sleep-disabled | Sleeping when idle was turned off |
service.sleep-enable-failed | Sleeping when idle could not be turned on |
service.slept | The service went to sleep after its idle period |
service.woke | The service was woken and is serving again |
service.wake_failed | The service could not be woken |
So are the domains attached to it:
| Event | What it means |
|---|---|
domain.verified | Ownership of the domain was confirmed |
domain.tls.issued | The certificate for the domain was issued |
domain.tls.failed | The certificate could not be issued |
domain.www-tls.issued | The certificate for the www redirect was issued |
domain.www-tls.failed | The certificate for the www redirect could not be issued |
domain.routing.active | Traffic for the domain is being routed to the service |
domain.routing.failed | Routing for the domain could not be set up |
domain.removed | The domain was detached |
domain.governance.mirror-failed | The domain could not be registered with the platform |
Events carry the same timestamps as the metrics, so deploys and failures are drawn straight onto the charts.
Read the metrics
- Open the environment’s metrics page and pick a window — 15 minutes, 1 hour, 6 hours, 24 hours or 7 days. Lessly picks the sampling step to match, so a chart always has roughly the same number of points whether it covers a quarter of an hour or a week.
- Select the targets, or leave everything selected. Metrics are collected for your services and your managed services; nothing selected means everything is shown.
- Read the summary cards at the top: requests per second across the selected services, the worst p95 among them, the worst error rate, and current CPU and memory. The two “worst” cards ignore services receiving almost no traffic, so a single idle service cannot dominate the number with a meaningless percentile.
- Read the charts below — request rate, latency with p50, p95 and p99 on one chart, error rate, CPU, memory and network throughput — then the per-service breakdown table.
- Use the deploy markers to answer “did my last release make this worse”. Deploys are drawn onto the charts as vertical markers: one where a release became healthy, and one for each build failure, deploy failure or failed health check. The markers can be turned off.
MCP: deployment_metrics_get.
Resource metrics exist for every target:
| Metric | Unit | Notes |
|---|---|---|
| CPU | cores | Aggregated across the replicas of a service |
| Memory | bytes | Aggregated across the replicas of a service |
| Network in | bytes per second | Traffic entering the target |
| Network out | bytes per second | Traffic leaving the target |
| Disk | bytes | Managed services only |
Where a service has a CPU or memory limit configured, the page also knows that limit and shows consumption as a percentage of it — see Scale a service.
HTTP metrics exist for services that answer HTTP requests. Managed services have none.
| Metric | Unit | What it is |
|---|---|---|
| Requests per second | requests/s | The rate of requests reaching the service |
| p50, p95, p99 | milliseconds | Latency percentiles |
| Error rate | percent | The share of responses with a 5xx status |
HTTP is measured on the traffic as it arrives from the internet, not inside your container. The latency you see is the one your users experience, network time included — it is always a little higher than what your own framework reports. And the percentiles are computed from bucketed measurements, so p99 in particular is an approximation, good for watching a trend rather than for quoting an exact number.
Why a view is empty, approximate or lagging
Both the log feed and the metric charts can look empty or wrong when nothing is broken. These are the reasons.
| What you see | Why |
|---|---|
| No HTTP series at all | Nothing has hit the service. This is not an error and the page does not treat it as one. |
| The right-hand edge of an HTTP chart lags the logs | Measurements take a minute or two to arrive. |
| A chart covering less than the window | The service is newer than the window. |
| The log result marked approximate | The feed reads a bounded window. When a range holds more lines than the window, what you see is the most recent part of it, and counts in the histogram and on the chips are lower bounds. |
| Nothing older than 24 hours in the log feed | The feed reaches back 24 hours. Anything older is not searchable here; if you need to keep output for longer, ship it somewhere yourself. |
| A refresh returning the same lines | The window is briefly cached, so two people looking at the same environment and range see the same feed. |
| A warning about one source, with the rest of the feed intact | One source was temporarily unreadable — database logs, most often. A failure to read one source never empties the feed. |
| The same line twice | Lines are not deduplicated. Two replicas printing the same text produce two entries. |
| Two static sites reported as one | Static-site HTTP metrics are counted per hostname. Two static sites published under the same hostname on different paths are reported together, under that hostname. |
Set an alert rule
An alert rule watches one metric on one target and fires when it crosses a threshold. Create rules on the environment’s alerts page, or straight from a target on the environment canvas.
- Pick the target — a service or a managed service in this environment.
- Pick the metric — CPU, memory, network in or network out; for a managed service, also disk.
- Pick the condition — above or below the threshold.
- Set the threshold, a number in the metric’s own unit. Disk is the exception: its threshold is a percentage of the database’s storage capacity, so “disk above 80” means the disk is more than 80 percent full.
MCP: deployment_alert_create, deployment_alert_list, deployment_alert_update, deployment_alert_delete, deployment_alert_events_list. Token scope: a key with write access to the environment’s alerts.
Alert rules cover resource metrics only. Requests per second, latency and error rate cannot yet be alerted on. Every rule can be disabled without deleting it, which is the usual way to mute a known problem.
How a rule fires
Lessly evaluates every enabled rule every two minutes, comparing the latest available measurement against the threshold. A rule is in one of two states, ok or firing, and only the transitions between them are recorded — a rule that stays broken for an hour produces one entry, not thirty.
| Transition | When it is recorded |
|---|---|
| firing | The rule was ok and the measurement crossed the threshold. |
| resolved | The rule was firing and the measurement came back. |
Each transition is kept as an event with the value that triggered it and the threshold it crossed. Firing rules are visible where you are already looking: the alerts page lists every rule with its current state, the environment canvas marks a target that has a rule firing against it, and the alerts page keeps a chronological list of recent firings and resolutions with their values.
Nothing is delivered to email, chat or a webhook. An alert reaches you when you open the environment, not before. If a rule has no measurement to evaluate — a service with nothing running, for example — it is left in its current state rather than being flipped either way.
A service that sleeps when idle is the one deliberate exception. While it is asleep or waking, a rule with a below condition against it is skipped entirely: zero traffic and zero CPU are the whole point of sleeping, so a sleeping service does not spend the night firing every “below” rule you set on it. Rules with an above condition are unaffected, and so is every rule on a service that does not sleep.
Next steps
- Scale a service: turn what the charts show into a replica count or a memory limit.
- Run a command inside a service: go inside when the logs have told you as much as they can.
- Choose where state lives: read a managed database’s own log, and watch its disk with an alert rule.
- Roll back a deploy: act on a deploy marker that made everything worse.
- Understand how deployment works: what a deploy does between
deployment.queuedanddeployment.healthy.