Skip to main content
Use this page to read CVM logs from your own scripts, monitoring tools, or clients written in any language. You request a signed log URL from the Phala Cloud API, then read the log stream from that URL with a plain HTTP GET. For the dashboard log viewer, see Serial Logs and Docker container logs. The phala logs command is described in the CLI reference.

Get a log URL

Call GET /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):
A container log URL is built from the base and the container name:
You can also read a ready-made container URL from 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, or sig parameters. 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-002 or ERR-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 with 400. 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

For a container (api in this example):

Polling

Poll on a timer. On each tick, fetch a fresh URL and request the last N 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 with follow=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:
  1. Fetch a fresh URL from the CVM API.
  2. Reconnect without repeating lines you already read:
    • Node logs (serial, stdout, stderr): reconnect with tail=0.
    • Container logs: request with timestamps=true, and reconnect with since set to the timestamp of the last line you received. Drop lines at that boundary second that you already saw.
  3. After several consecutive failures, give up and report the error. The phala CLI stops after 5. A successful 200 response resets the failure count.
Node log streams can contain heartbeat lines whose entire content is [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

The loop fetches a new URL on every connection, so an expired signature (401) is handled by the next iteration.

Errors

Error responses have a JSON body:
Match on 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.
  • 429 and 5xx: back off, fetch a fresh URL, and retry.
  • 400, 404, 409: do not retry blindly. Fix the request or the CVM state first. With ERR-17-005, node logs (serial, stdout, stderr) remain readable.
When you contact support, include the 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 with 400.