Please walk me through taking say, the default liv...
# ask-ai
m
Please walk me through taking say, the default livekit cloud starter project, to being able to use egress?
t
Thanks for reaching out, Alexander. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I can walk you through that. Which starter project are you using (Node, Python, or Go)? Also tell me whether you want egress to a file (S3/local), RTMP, or a direct stream (track/room composite). Sources: Deploying to production | LiveKit Docs | Universal Egress | server-sdk-js | livekit/livekit_composite | DeepWiki
m
Let’s say python. And I want to egress to a GCP bucket.
t
Great — I’ll walk you through taking the Python starter agent to egressing recordings into a GCP bucket. I’ll assume you’re using the agent-starter-python template and want a Room Composite MP4 output to GCS. If you want a different egress type (track/RTMP) tell me and I’ll adjust. Prerequisites • LiveKit Cloud project and CLI access (you’ve run
lk cloud auth
). • agent-starter-python cloned locally. • A GCP project with a storage bucket and a service account JSON key that has "Storage Object Creator" for that bucket. Steps 1) Add server SDK to the project • In your agent project, install the server SDK and dotenv:
Copy code
uv add livekit-api python-dotenv
2) Add GCP credentials and LiveKit secrets • Place your service account JSON in the project (e.g.,
./gcp-sa.json
) or ensure Cloud Run/Cloud Run service has workload identity set. • Create a
.env
(local dev) with:
Copy code
LIVEKIT_URL=wss://<your-project>.livekit.cloud
  LIVEKIT_API_KEY=<your_api_key>
  LIVEKIT_API_SECRET=<your_api_secret>

  GCP_PROJECT_ID=<your-gcp-project-id>
  GCP_BUCKET_NAME=<your-bucket-name>
  GOOGLE_APPLICATION_CREDENTIALS=./gcp-sa.json
• For production on LiveKit Cloud, inject these as agent secrets using the LiveKit CLI instead of committing files. 3) Create a standalone egress trigger script (not inside the agent runtime) • Example
start_egress.py
(async Python using
livekit-api
):
Copy code
import os, asyncio
from dotenv import load_dotenv
from livekit import api

load_dotenv()

async def start_room_egress(room_name: str):
    lk = api.LiveKitAPI(os.getenv("LIVEKIT_URL"),
                        api_key=os.getenv("LIVEKIT_API_KEY"),
                        api_secret=os.getenv("LIVEKIT_API_SECRET"))

    req = api.StartRoomCompositeEgressRequest(
        room_name=room_name,
        file_output=api.EncodedFileOutput(
            file_type=api.EncodedFileType.MP4,
            filepath=f"{room_name}_recording.mp4",
            output=api.GoogleCloudStorageUpload(
                bucket_name=os.getenv("GCP_BUCKET_NAME"),
                project_id=os.getenv("GCP_PROJECT_ID"),
            ),
        ),
        layout="speaker-dark"
    )

    resp = await lk.egress.start_room_composite(request=req)
    print("egress_id:", resp.egress_id)
    await lk.close()
    return resp.egress_id

if __name__ == "__main__":
    asyncio.run(start_room_egress("test-room"))
• Replace
room_name
with the active room you want to record. 4) Optionally trigger egress automatically via webhook • Deploy a small webhook endpoint (Flask/FastAPI) to receive LiveKit events and call the script when
room_started
occurs. • Register the webhook URL in your LiveKit Cloud project. • Use
livekit.api.WebhookReceiver
to validate incoming webhook payloads and then call the same
start_room_egress(...)
logic. 5) Run and test locally • Start your agent locally (dev mode) so a room can be created:
Copy code
uv run src/agent.py dev
• Join the room from your client. • Run
python start_egress.py
to start egress to your GCS bucket. • Check GCS for
test-room_recording.mp4
. 6) Deploy agent and webhook to LiveKit Cloud • Use
lk agent create
(or
lk agent update
) to deploy your agent and webhook. • Inject secrets via the CLI rather than `.env`:
Copy code
lk agent update --secrets-file secrets.txt
where
secrets.txt
contains the environment values (omit the local JSON file — use Cloud secret management or workload identity in GCP). 7) Monitor and control egress • Use
lk agent logs
and
lk agent status
to view agent health. • Programmatically list/stop egress via
lk_api.egress.list_egress()
and
lk_api.egress.stop_egress(egress_id=...)
from the same server SDK. Notes & tips • The egress call runs from a server-side process (your control plane). The agent itself participates in the room but the egress start is initiated via the server SDK or CLI. • Ensure the service account key or runtime identity used has permissions to write to the target GCS bucket. • For production, prefer GCP workload identity or secret injection rather than bundling JSON keys. • File outputs (MP4/WebM/HLS) support GCS uploads. RTMP uses different output configuration. If you want, I can: • Provide a complete webhook server example (Flask) wired to call egress on
room_started
. • Show how to configure LiveKit agent secrets for Cloud deployment. • Switch the example to track egress or RTMP output. Which follow-up would you like—webhook example, deployment secrets steps, or a track/RTMP example?
m
What about for node js?
t
Thanks — I’ll walk you through the Node.js starter to egress recordings into a GCP bucket. I’ll assume agent-starter-node and you want a Room Composite MP4 uploaded to GCS via its S3-compatible endpoint. If you prefer Track/RTMP egress or a different layout, tell me and I’ll adjust. Prerequisites • LiveKit Cloud project with API key/secret and CLI access (
lk cloud auth
). • agent-starter-node cloned and Node installed. • GCP bucket created. Create an HMAC key for the bucket (S3-compatible) or use a service account + S3-compatible settings. HMAC keys give
GCS_ACCESS_KEY
and
GCS_SECRET
. • livekit-server-sdk available in your project. Steps 1) Install dependencies • In the agent project:
Copy code
pnpm install
  pnpm add livekit-server-sdk
2) Add environment variables • Local `.env`:
Copy code
LIVEKIT_URL=wss://<your-project>.livekit.cloud
  LIVEKIT_API_KEY=<api_key>
  LIVEKIT_API_SECRET=<api_secret>

  GCS_ACCESS_KEY=<hmac_access_key>
  GCS_SECRET=<hmac_secret>
  GCS_BUCKET_NAME=<your-bucket>
• In production, inject secrets via the LiveKit CLI (don’t commit
.env
). 3) Start Room Composite egress from Node (example) • Use
EgressClient
to start egress (S3 output configured for GCS endpoint). Example snippet to run when you want to start recording:
Copy code
import { EgressClient } from "livekit-server-sdk";

const egressClient = new EgressClient(process.env.LIVEKIT_URL!, {
  apiKey: process.env.LIVEKIT_API_KEY!,
  secret: process.env.LIVEKIT_API_SECRET!,
});

const req = {
  roomName: "my-room",
  layout: "speaker-dark",
  file: {
    fileType: "mp4",
    output: {
      s3: {
        accessKey: process.env.GCS_ACCESS_KEY!,
        secret: process.env.GCS_SECRET!,
        endpoint: "<https://storage.googleapis.com>",
        bucket: process.env.GCS_BUCKET_NAME!,
        region: "auto",
        forcePathStyle: false
      }
    }
  }
};

try {
  const info = await egressClient.startRoomCompositeEgress(req);
  console.log("egress started:", info.egressId);
} catch (err) {
  console.error("egress error:", err);
}
4) Where to trigger egress • Option A (inline): start egress from your agent code when the agent joins the room. • Option B (recommended): run egress from an external control service (webhook or small backend) to decouple recording from agent lifecycle. 5) Webhook-based automatic start (recommended) • Create a webhook endpoint to receive LiveKit events and validate using
WebhookReceiver
. • On
room_started
, call the egress client with the same request above. • Deploy that webhook service and register its URL in your LiveKit Cloud project. 6) Test locally • Start the agent:
Copy code
uv run src/agent.ts dev
• Join the room from a client. • Trigger egress (either via your webhook or run the egress call). • Check GCS bucket for the MP4 file. 7) Deploy to LiveKit Cloud • Use LiveKit CLI:
Copy code
lk cloud auth
  lk agent create
• Inject secrets instead of shipping `.env`:
Copy code
lk agent update --secrets-file secrets.txt
where
secrets.txt
contains the env variables (omit local JSON keys; use HMAC or GCP secret patterns). 8) Monitor & control • Use
lk egress list
/
lk egress info <id>
or the SDK’s
listEgress()
/
stopEgress()
to manage runs. • Use webhooks for
egress_started
and
egress_ended
events for post-processing. Notes & tips • GCS via S3 endpoint requires HMAC keys or appropriate S3-compatible credentials. If you prefer direct GCS uploads (via service account), you must ensure the runtime has credentials and LiveKit supports GoogleCloudStorageUpload in server SDK patterns (S3 is commonly used via endpoint). • For production, prefer workload identity or secret injection rather than storing keys in files. • If you want automatic retention rules, post-processing, or custom layouts, I can show examples. Next step — which would you like: • A complete webhook example (Express) that validates events and starts egress? • A ready-to-deploy agent code patch (where to place the egress call inside agent-starter-node)? • Track or RTMP egress example instead of Room Composite? Sources: server-sdk-js | livekit/livekit_composite | DeepWiki | Deploying to production | LiveKit Docs | Universal Egress
m
My current architecture is such that the client (in my case an iOS app) requests a token from the backend to authenticate and join a livekit room - which I believe gets created automatically by the backend when it creates the token. Do I update the egress parameters in the backend token generation function?
t
Thanks — good question. Short answer: you generally do NOT add egress parameters to the client token. Egress is normally started from a trusted server process (using the server SDK or CLI) that has API credentials. Token generation for clients should remain focused on joining the room and user permissions. Quick guidance and options: • Typical / recommended flow ◦ Backend issues a Join token to the iOS client (no egress config). ◦ A separate server-side controller (same backend or a different service) calls the LiveKit server SDK’s egress start API when you want to record (e.g., on room start, via webhook, or on explicit user action). That controller uses your LIVEKIT_API_KEY/SECRET. • When to include egress in a token ◦ You can include an egress grant in a token if you explicitly want a client or an agent to be allowed to start egress from the client-side. This is uncommon because egress start requires elevated privileges and you usually keep that on the server. ◦ If you do this, only issue such tokens to trusted services (not general mobile clients). • Implementation notes (Node.js examples) ◦ Recommended: keep your existing token endpoint as-is; add a server endpoint or webhook handler that does: i. Initialize the server SDK/Egress client with LIVEKIT_API_KEY/SECRET. ii. Call startRoomCompositeEgress / startTrackEgress / startRTMPEgress with GCS/S3 output settings. ◦ Optional (less recommended): add an egress grant when creating a token: ▪︎ Token creation libraries allow adding an egress grant; the client could then call an API that uses that grant. Only use for trusted services. If you want, I can: • Show a concise Node.js example that starts egress from your backend (using the same service that issues tokens). • Show how to add an egress grant to a token (with a short example) and explain risks. Which would you prefer?
m
Best format for audio only recording? No need for video because there will be no video feed from clients.
t
Thanks — good question. For audio-only where clients send no video, use an audio container/codec that preserves quality and broad compatibility: Recommendations • Best choice: Ogg/Opus — high quality at low bitrates, ideal for conferencing/voice, preserved by LiveKit track egress as raw audio. Good for speech analytics. • If you need wide compatibility (players, browsers): MP4 (AAC) or WebM (Opus in WebM). MP4/AAC is very portable for downstream consumers. • If you need single-channel raw audio for processing: WAV (PCM) — large files but simplest to process. How to implement (server-side egress — do NOT put egress params in client join token) • Start a Track Egress (single participant audio) or Room Composite but request audio-only output. • Send output to GCS via S3-compatible output (GCS endpoint) or GoogleCloudStorageUpload if SDK supports it. Node.js example — audio-only MP4 to GCS (S3 output)
Copy code
import { EgressClient } from "livekit-server-sdk";

const egressClient = new EgressClient(process.env.LIVEKIT_URL!, {
  apiKey: process.env.LIVEKIT_API_KEY!,
  secret: process.env.LIVEKIT_API_SECRET!,
});

const req = {
  roomName: "my-room",
  // For audio-only, use track egress or room composite with audio-only file type
  file: {
    fileType: "mp4", // or "webm", "ogg"
    output: {
      s3: {
        accessKey: process.env.GCS_ACCESS_KEY!,
        secret: process.env.GCS_SECRET!,
        endpoint: "<https://storage.googleapis.com>",
        bucket: process.env.GCS_BUCKET_NAME!,
        region: "auto",
      }
    }
  },
  audioOnly: true, // if supported by your SDK request shape; otherwise use track egress
};

const res = await egressClient.startRoomCompositeEgress(req);
console.log("egress id", res.egressId);
Python example — track egress to OGG/Opus in GCS (preferred for voice)
Copy code
from livekit import api
import os, asyncio

async def start_track_egress(participant_identity):
    lk = api.LiveKitAPI(os.getenv("LIVEKIT_URL"),
                       api_key=os.getenv("LIVEKIT_API_KEY"),
                       api_secret=os.getenv("LIVEKIT_API_SECRET"))

    req = api.StartTrackEgressRequest(
        room_name="my-room",
        participant_identity=participant_identity,
        output=api.TrackEgressRequestFileOutput(
            file=api.EncodedFileOutput(
                file_type=api.EncodedFileType_OGG,  # OGG/Opus
                filepath=f"{participant_identity}_audio.ogg",
                output=api.GoogleCloudStorageUpload(
                    bucket_name=os.getenv("GCP_BUCKET_NAME"),
                    project_id=os.getenv("GCP_PROJECT_ID"),
                )
            )
        )
    )
    resp = await lk.egress.start_track_egress(request=req)
    print("egress id", resp.egress_id)
    await lk.close()

# call with asyncio.run(start_track_egress("user123"))
Notes and tips • Use Track Egress when you only need a single participant’s raw audio. Use Room Composite/Track Composite when you want mixed audio of the whole room. • Choose Ogg/Opus for voice quality and size. Choose MP4/AAC for maximum playback compatibility. • Keep egress control on the server with LIVEKIT_API_KEY/SECRET (don’t add egress params to client tokens). • Ensure GCS credentials (HMAC or service account) used by the server have write access to the bucket. Would you like a full Express/Flask webhook example that auto-starts audio-only egress on room_started?