Is my instance healthy?
This guide is for the person who runs a Lakana instance. It lists what to look at to tell
whether the instance works, the command or address that answers each question, and what a bad
answer looks like. The commands are for the Compose bundle (Deploying with Compose);
lakana worker list and the others also work against any other instance you have an
administrator's access to.
After this guide you can check the services, the workers, the queue, the catalog database, the object storage and the disk, you can read the one line a stuck or failed run gives you, you know which lines of the logs matter, and you know what to collect before you ask a question in public.
The short version
Run these in order. The first that looks wrong is where to read on.
docker compose ps --all: every service isrunningandhealthy, and the one-off services (migrate,bucket,setup) haveExited (0). (The services)- The address
/healthzanswers. (The health route) lakana worker list: as many workers arealiveas you started. (The workers)lakana query list --env dev --status queueddoes not grow. (The queue)- The disks have room. (Disk space)
Nothing here is a service level, and none of it replaces looking at what your own people notice: a dashboard that is slow or a job that did not run is a signal too.
The services
From the directory of the bundle:
docker compose ps --all
What each line tells you:
| Service | Healthy means | It does not mean |
|---|---|---|
postgres |
The database accepts connections on its network address. | That the catalog inside it is intact, or that it has room left. |
seaweedfs-core |
The storage's master answers and its file service accepts connections. | That the volume has room left. |
seaweedfs |
The S3 gateway, the only way to the data, answers HTTP. | That the credentials and the lakana bucket work. |
server |
The server answers /healthz (next section). |
That the database, the storage or any worker works. |
worker (and worker-interactive, worker-pipeline) |
The worker's process is alive. | That it has reached the database, registered, or takes work. |
The one-off services migrate, bucket and setup run once and end; Exited (0) is their
healthy state. One that exited with another number is the reason the services behind it did not
start: read its log with docker compose logs migrate (or bucket, setup). A service that
restarts again and again shows Restarting; its log says why.
The health route
The server has one health route, /healthz. It needs no sign-in, so a script, a load balancer or
Docker can ask it:
curl -s "$URL/healthz"
$URL is the public address of the instance. It answers:
{"status":"ok"}
That proves one thing: the server process is running and answers HTTP. It does not look at the
database, the storage or the workers, so a server that answers can still fail every query. The
image's own check asks the same route from inside the container every 10 seconds, which is what
the healthy of the server service in docker compose ps comes from. There is no second route
that checks the database or the storage; the rest of this page is how you check them.
A server that does not answer at all is a stopped or restarting container, a proxy or a firewall
in front of it, or a wrong address. docker compose ps and docker compose logs server tell
which.
The workers
A worker is the process that runs queries and tasks. It reports in to the database every few
seconds. It is alive while its last report is recent, and lost once it has been silent for
60 seconds (LAKANA_RUN__LEASE_SECONDS). A lost worker's row stays for an hour, and what it was
running is ended by the worker that holds the scheduler role. The commands need the right to
manage environments on the instance, which an administrator has:
lakana worker list
lakana scheduler status
Healthy looks like this:
- As many
aliverows as you started.LAKANA_WORKERSplusLAKANA_WORKERS_INTERACTIVEplusLAKANA_WORKERS_PIPELINEin the bundle's.env. Fewer means a worker did not start or cannot reach the database; a row with the statuslostis one that stopped reporting. - Every kind of work you use has an
aliveworker. The columntakessaysall,interactiveorpipeline. See Seeing that a kind has no worker. - The workers take work. The column
doingshowsidleor the query, deploy or task a worker runs. Analiveworker that staysidlewhile queries wait is the case to look into: the reason of each waiting query says why (see A stuck run). - One worker holds the scheduler role.
lakana scheduler statusnames it, and when it last reported. The role ends what lost workers held and runs the hourly sweep, so a holder that has not reported for much longer than a minute, or no holder, is a problem: When a worker is lost says what frees it.
An empty list prints No worker is registered. followed by what to do. With the Compose bundle
that is docker compose logs worker first.
The queue
A healthy instance has queries and runs that start soon after they are submitted. Two listings show what waits; run them now and again after a few minutes. A queue that is short and changes is normal; one that only grows is not.
lakana query list --env dev --status queued
lakana runs list --env dev --status queued,running
Use the name of your own environment instead of dev. You see your own queries; with
MANAGE_ENVIRONMENT on the environment you see everyone's. The waiting_for column of the query
listing gives the reason each one waits. To see what failed in the last day, use this listing (on macOS, write date -u -v-1d +%Y-%m-%dT%H:%M:%SZ
instead of the date command shown):
lakana runs list --env dev --status failed --since "$(date -u -d '1 day ago' +%Y-%m-%dT%H:%M:%SZ)"
More filters and paging: Find a run, a query or a deploy.
The catalog database and the object storage
Two things the instance cannot work without. Neither has a command of its own; you read them from what else works.
The catalog database. docker compose ps shows postgres as healthy when it accepts
connections. A command that reads the catalog, such as lakana worker list, answers only when
the server reaches the database, so an answer proves it. When the database goes away:
- every request that needs it fails, and a new server container does not start (the
migrateservice runs first and exits with an error); - each worker logs
The worker's heartbeat could not be written; it is tried again every 5 s.and, after a minute without success,No heartbeat was written for 60 s (LAKANA_RUN__LEASE_SECONDS): this worker claims nothing and stops the query it runs until one is written. Check that the database is reachable.Its queries and deploys are then ended as lost. When the database is back, it logsThe worker's heartbeat is fresh again; it claims work again.
The object storage. seaweedfs being healthy shows the gateway answers. Whether the
instance can read and write its data you only learn by doing it: run a query that reads a table
of yours. The tutorial's query is an example; use a table you have:
lakana sql "SELECT * FROM gold.station_ranking ORDER BY warmth_rank"
When the storage cannot be reached, a query that reads a table fails instead of returning rows,
and a request that stores or sends an artifact file answers that the storage could not be written
or read and that the operator should check whether it is unreachable. Read docker compose logs
seaweedfs and docker compose logs seaweedfs-core.
Disk space
Lakana itself reports no disk use and sends no alert, so look at the disk yourself. The bundle keeps its state in three named Docker volumes, all on the disk that holds Docker's data:
| Volume | Holds | What fills it |
|---|---|---|
pgdata |
The catalog (PostgreSQL). | Table, run and audit history. |
seaweed |
The data of every table and every artifact. | Your data. |
worker-cache |
At /home/lakana/.cache/lakana-worker: the Python environments of the projects and the download cache of uv, and the working directories of the tasks that run. |
Every package version a project locks; the download cache is never trimmed. |
The worker containers also keep temporary files under /var/tmp/lakana, inside the container.
The sweep removes what a worker that was killed left behind, once an hour
(LAKANA_RUN__SWEEP_INTERVAL).
docker system df -v
docker compose exec worker df -h /home/lakana/.cache/lakana-worker
The first command lists every volume with its size. The second shows how full the disk under
the workers' cache is. Also look at the disk of the machine itself with df -h on the host.
How to reclaim the download cache of the workers is in Python assets.
The documentation gives no figure for how much space your data needs; measure on your own data.
A stuck run and why
A query or a task that has not started says why, in one sentence, and a task that failed has a code, a cause and a next step. Both are shown where you look at the run:
- Waiting.
lakana query status QUERY_ID --env devshowswaiting_forfor a queued query, andlakana runs show RUN_ID --env devshows it for each task that waits. Most reasons pass by themselves. The ones that need you areno_workerandno_worker_of_kind(start a worker), and a reason that does not change for a long time. All of them, and what to do, are in Why is my query or task waiting?. - Failed.
lakana runs show RUN_ID --env devshows the code of each failed task and under the table the cause andNext:. The codes that point at the instance, rather than at a project, are these; all of them are in Why did it fail?:
| Code | What it says about the instance |
|---|---|
worker_lost |
The worker running the task stopped before it finished: it was killed, lost power or its network, or was stopped for memory. Look at the worker's log and memory limit. |
memory |
The task used more memory than the worker allows. Raise LAKANA_WORKER_MEMORY or make the task handle less data: Memory limits. |
no_runtime |
This worker cannot run this kind of task, for example because it has no Python runtime. The detail names what is missing. |
code_changed |
The deployed code is missing or no longer matches what was deployed. Deploy the project again; if it repeats, check the storage. |
internal |
The platform failed in a way it did not expect. The worker's log has the traceback. |
Log lines that matter
Lakana's own records go to standard error, one per line with the time, the level and the logger
name; the web server's lines (startup and, if switched on, one per request) have a shorter form
without the time. docker compose logs shows both together. Read them with docker compose logs server and docker compose logs worker; the default
level, INFO, shows all of the lines below. How to change the level and what the lines of the
sweep mean: The sweep and
After a restore.
| Where | The line starts | What it means and what to do |
|---|---|---|
| worker | Memory limit exceeded: the worker and its tasks use |
The worker passed 90 percent of its memory limit and ended its largest query or task, or stopped itself. Narrow the work or raise LAKANA_WORKER_MEMORY: Memory limits. |
| worker | The worker's heartbeat could not be written |
The worker cannot reach the database (see above). The worker's heartbeat is still failing repeats it, and No heartbeat was written for says the worker has stopped claiming work. |
| worker | Task run <id> is no longer ours; its outcome is dropped. |
The worker's claim on a task had run out when the task finished, so the worker dropped the result. Look for worker_lost on the run. |
| worker | Lease renewal of task run <id> failed; retrying. |
The worker could not extend its claim on a task. If it keeps failing, the task is taken from it. |
| worker | Polling iteration failed; continuing. |
The worker's loop that looks for work hit an error, followed by the error itself. Occasional lines are harmless; a stream of them is not. |
| worker | Running task run <id> failed unexpectedly., Deploy run <id> failed unexpectedly. |
The platform failed in a way it did not expect. The run shows the code internal and says to see the worker log; the traceback follows the line. |
| worker | Python assets: disabled: |
At start-up: this worker cannot run Python tasks; the line ends with To enable it: and the fix. |
| server | The grants that have no effect could not be listed at start-up (a database error) |
The database answered with an error while the server started. Start-up goes on; check the database. |
| server | A start-up that stops with a message that names a setting | A setting has a value the program does not accept. The message names it; correct it in compose.yaml or .env. |
| server | An access line that ends 500 Internal Server Error or 503 |
The server could not answer a request (a 503 asks the client to try again, for example when the storage could not be opened). The lines around it have the cause. |
What to collect before you ask
Questions and bug reports go on a public page (Support), where anyone can read them, so collect what lets a stranger understand your case and nothing that lets a stranger into your instance. In this order:
1. The versions. The CLI's and the instance's:
lakana --version
2. The state of the services. The output of docker compose ps --all, and the image you run:
the LAKANA_IMAGE line of .env.
3. The failing run or query. Its id and, for a run, the code from lakana runs show. Not the
SQL or the data if either is private.
4. The relevant log lines, with secrets removed. This writes the last hour of both logs to a
file and replaces two kinds of secret: an access token (it starts lakana_pat_; the server never
logs one itself, but a tool or a person may have pasted one), and a password in a connection
address:
docker compose logs --no-color --since 1h server worker \
| sed -E -e 's/lakana_pat_[^[:space:]]+/lakana_pat_REDACTED/g' \
-e 's#(://[^:/@[:space:]]+:)[^@[:space:]]+@#\1REDACTED@#g' > lakana-logs.txt
Open the file and read it before you post it: the filter knows two patterns, not your data. A
table name, a file path, a person's name or a SQL statement can be in a line, and a package index
address can carry a credential (user:token@ in its address). Cut a line or replace a word when
in doubt.
Never post the .env file, the output of docker compose config (it prints the values of
.env), a token, the master key, or the storage keys. If you posted one by mistake, treat it as
public: replace the token (Give a second person access),
and for the master key and the storage keys start with Manage secrets.
Lakana is run by one maintainer, and a question gets an answer when time allows or not at all; Support says where to ask and what the project does and does not provide. A report with these four things in it is one that can be understood without asking you for more.
See also
- Run Lakana for other people: the whole job of an operator, in order.
- Why is my query or task waiting? and Why did it fail?.
- Deploying with Compose: the services, the memory limit, the sweep and the logs.
- The audit log: who did what.