phala logs command is described in the CLI reference.
Get a log URL
CallGET /api/v1/cvms/{id} on https://cloud-api.phala.com with your API key in the X-API-Key header. The response contains a logs object:
A node log URL has this form (
serial, stdout, or stderr in the last path segment):
log_endpoint on each container in the response of GET /api/v1/cvms/{id}/composition.
In API version 2025-10-28, the serial URL is returned as syslog_endpoint. Later versions return it as logs.serial.
All of these fields are null when the requesting user cannot read the CVM’s logs.
The container fields (logs.container_log_base, logs.auth_query, and the container log_endpoint) are also null unless the CVM has Public Logs enabled. Container requests are rejected with ERR-17-006 when it is disabled. Node logs (serial, stdout, stderr) are not affected by this setting.
How URLs are signed
Phala Cloud signs each URL for the user who requested it. The signature covers the whole CVM, so one signature works for the serial, stdout, stderr, and container URLs. Your client never signs anything.- Do not change the
uid,exp,kid, orsigparameters. Add your own query parameters after them. - Anonymous access is not supported. You must be a member of the CVM’s workspace to get a URL.
- Treat a URL like a credential until it expires. Anyone who has it can read the logs.
Expiry and rotation
Because of these limits:
- Fetch a fresh URL from the CVM API right before each connection.
- Never cache or store URLs.
- If a request returns
401(ERR-17-002orERR-17-003), the signature is missing, invalid, or expired. Fetch a new URL and retry.
Query parameters
The same parameters apply to every log kind unless noted. Unknown or repeated parameters are rejected with400. Boolean parameters accept only true or false.
Append parameters with
& after the signature parameters. For a container URL, auth_query is already in the query string, so join with & as well.
Read logs
Fetch recent lines
api in this example):
Polling
Poll on a timer. On each tick, fetch a fresh URL and request the lastN lines without follow. Use since for container logs, or compare against the last line you saw, to avoid processing the same lines twice.
Following
Request the URL withfollow=true and read the response as it arrives. The stream can end for any reason: a normal close, the 1-hour limit, or a network error. When it ends:
- Fetch a fresh URL from the CVM API.
- Reconnect without repeating lines you already read:
- Node logs (
serial,stdout,stderr): reconnect withtail=0. - Container logs: request with
timestamps=true, and reconnect withsinceset to the timestamp of the last line you received. Drop lines at that boundary second that you already saw.
- Node logs (
- After several consecutive failures, give up and report the error. The
phalaCLI stops after 5. A successful200response resets the failure count.
[vmm heartbeat] or [teepod heartbeat]. Drop those lines from node logs only; keep container lines with the same text, and keep empty lines, which can be real log entries. No other keepalive records are added to the stream.
Example: follow a serial log in Python
401) is handled by the next iteration.
Errors
Error responses have a JSON body:error_code. The message text is for people and may change.
How to react:
401(ERR-17-002,ERR-17-003): fetch a fresh URL from the CVM API and retry.429and5xx: back off, fetch a fresh URL, and retry.400,404,409: do not retry blindly. Fix the request or the CVM state first. WithERR-17-005, node logs (serial,stdout,stderr) remain readable.
error_code, the time of the request, and the CVM ID.
Migrate from old URLs and parameters
Older dstack-style log URLs accepted different parameters. Requests that still send them now fail with400.

