im have self hosted agent, once the container is d...
# ask-ai
f
im have self hosted agent, once the container is deployed, i dont see any logs saying that proccess is intialezed or workers are registered although livekit cloud shows agent worker. however the agent never joins the room
t
Thanks for reaching out, Palwisha. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I’m sorry you’re hitting this. I’m responding to the points you raised: the container runs, you don’t see any agent initialization / worker-registration logs, LiveKit Cloud shows a worker, but the agent never joins the room. Let’s narrow this down and get you unstuck. First, quick checklist to verify common causes (please run these and share results): 1. Confirm the container entrypoint / CMD ◦ What command does the container run? (Give the exact Dockerfile CMD or Kubernetes container command.) ◦ It must run the agent CLI in start/production mode (for example:
uv run agent.py start
or
node agent.js start
). If you’re running the script without the
start
argument the agent may never register. 1. Verify environment variables (auth & URL) ◦ Inside the running container, run: ▪︎ Docker:
docker exec <container> env | grep LIVEKIT
▪︎ K8s:
kubectl exec -it <pod> -- env | grep LIVEKIT
◦ Share whether
LIVEKIT_URL
,
LIVEKIT_API_KEY
(or
API_KEY
), and
LIVEKIT_API_SECRET
(or
API_SECRET
) are present and their values (redact secrets — just tell me the host/format and that keys exist). 1. Check logs directly from the process ◦ Provide output of
docker logs <container>
or
kubectl logs <pod>
(if those are empty, try
kubectl logs <pod> -c <container-name>
). ◦ If logs are filtered or blank, try increasing log verbosity: run with
--log-level=DEBUG
or set
logging.basicConfig(level=logging.DEBUG)
in Python. 1. Confirm the agent process is actually running inside the container ◦ Exec into container and run
ps aux
(or
pgrep -a python
/
pgrep -a node
) and tell me whether you see the agent process and the exact command-line used. 1. Confirm
cli.run_app(server)
/ server start code (if you control code) ◦ If you can share the agent entrypoint (few lines), confirm it calls the CLI run method at the end:
Copy code
if __name__ == "__main__":
         cli.run_app(server)
• If that call is missing the server will not start or register. 1. Confirm LiveKit URL format ◦ What value are you using for
LIVEKIT_URL
? It must be the WebSocket endpoint (e.g.,
wss://...
), not a dashboard/REST URL. If you prefer a short targeted test now: • Start the container locally (not in k8s) with log-level DEBUG and env vars set, then paste the logs. That will quickly show if the agent reaches registration or crashes early. What I can do once you share the above: • If CMD/entrypoint is wrong I’ll give the exact Dockerfile / k8s fix. • If env vars are missing or URL is wrong I’ll show how to inject them in Docker or k8s. • If logs show an exception, paste the log and I’ll interpret next steps. • If the agent process is running but never accepts jobs, I’ll walk through checking the job acceptance logic and join flow. If you can, please reply with: • Agent runtime (Python or Node) • Container CMD / Dockerfile snippet or k8s container command • Output of
env | grep LIVEKIT
(safe to redact secrets) • Recent container logs (or
kubectl logs
output) • Whether you see the agent process in
ps aux
I’ll use that to give precise steps. Sources: An open source stack for real-time multimodal AI | Server startup modes | LiveKit Documentation | Server options | LiveKit Documentation | Why is my Agent insights data missing? | LiveKit
f
dockerfile: # ============================================ # Stage 1: Builder - Install dependencies # ============================================ FROM python:3.13-slim AS builder ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PIP_NO_CACHE_DIR=1 \ PIP_DISABLE_PIP_VERSION_CHECK=1 WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ libffi-dev \ && rm -rf /var/lib/apt/lists/* COPY requirements.txt . RUN pip install --upgrade pip setuptools wheel && \ pip install --no-cache-dir -r requirements.txt # ============================================ # Stage 2: Runtime # ============================================ FROM python:3.13-slim ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ && rm -rf /var/lib/apt/lists/* COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages COPY --from=builder /usr/local/bin /usr/local/bin COPY . . CMD ["python", "interview_agent.py", "start"] main section iin interview_agent: if name == "__main__": cli.run_app( WorkerOptions( entrypoint_fnc=entrypoint, agent_name=os.getenv("LIVEKIT_AGENT_NAME", "interview-agent"), num_idle_processes=1, ) ) entrypoint func: async def entrypoint(ctx: JobContext): """Main entrypoint for the interview agent worker.""" logger.info(f"Starting interview agent job: {ctx.job.id}") openai_key = os.getenv("OPENAI_API_KEY") livekit_url = os.getenv("LIVEKIT_URL") if not openai_key: logger.error("Missing OPENAI_API_KEY — agent cannot initialize") return await ctx.connect() metadata = parse_job_metadata(ctx) if not metadata.get("candidate") or not metadata.get("role"): logger.error("Missing required metadata: candidate and role information") return interview_id = metadata.get("interview_id", "unknown") candidate_name = metadata.get("candidate", {}).get("name", "Candidate") duration_minutes = metadata.get("duration_minutes", 20) duration_seconds = duration_minutes * 60 # Start egress recording egress_id = await start_egress_recording(ctx.room.name, interview_id, livekit_url) # Create agent and session interview_agent = create_interview_agent(interview_metadata=metadata) logger.info(f"[AGENT] Starting session: llm={LLM_MODEL} stt={STT_MODEL} tts={TTS_MODEL}") session = AgentSession( vad=silero.VAD.load(), stt=openai.STT(model=STT_MODEL, language=STT_LANGUAGE), llm=openai.LLM(model=LLM_MODEL), tts=openai.TTS(model=TTS_MODEL, voice=TTS_VOICE), min_endpointing_delay=VAD_MIN_ENDPOINTING_DELAY, ) @session.on("user_speech_committed") def on_user_speech(msg: agents.llm.ChatMessage): if msg.content: logger.info(f"[USER] {msg.content}") @session.on("agent_speech_committed") def on_agent_speech(msg: agents.llm.ChatMessage): if msg.content: logger.info(f"[AGENT] {msg.content}") # Register room event listeners BEFORE session.start() to avoid race conditions start_event = asyncio.Event() disconnect_event = asyncio.Event() setup_room_events(ctx, start_event, disconnect_event, candidate_name) # Notify the frontend that the agent is ready to receive the START signal. # If the frontend sent START during agent setup (~7s window), it missed the # data_received listener. The frontend should resend START on AGENT_READY. try: await ctx.room.local_participant.publish_data( json.dumps({"action": "AGENT_READY"}).encode("utf-8"), reliable=True, ) logger.info("Sent AGENT_READY signal to room") except Exception as e: logger.warning(f"Failed to send AGENT_READY signal: {e}") await session.start( agent=interview_agent, room=ctx.room, room_input_options=RoomInputOptions(close_on_disconnect=False), ) await wait_for_start_signal(start_event, candidate_name) await run_interview(session, disconnect_event, candidate_name, duration_seconds) livekit_report = generate_livekit_report(ctx) transcript_entries = extract_transcript_entries(livekit_report or {}) logger.info(f"[CLEANUP] Extracted {len(transcript_entries)} transcript entries") # Disconnect room if still connected (timeout path) try: await ctx.room.disconnect() except Exception: pass run_cleanup(livekit_report, transcript_entries, metadata, interview_id, egress_id) requirements.txt: livekit>=1.0.20 livekit-agents>=1.3.6 livekit-plugins-openai>=1.3.6 livekit-plugins-silero>=1.3.6 livekit-api>=1.1.0 python-dotenv==1.2.1 boto3>=1.42.5 httpx>=0.27.0 livekit url and creds are present
t
Thanks — this is very helpful information. I’m responding to your report that the container runs with no agent logs, LiveKit Cloud shows a worker, but the agent never joins the room. From what you shared, the most likely cause is a startup-mode mismatch between your code and the Docker CMD. Why this is happening • Your code calls cli.run_app with WorkerOptions (the worker pattern). That pattern expects the process to run as a worker (no top-level
start
subcommand). • Your Dockerfile runs
python interview_agent.py start
. The
start
subcommand is for the AgentServer pattern (server mode), not the WorkerOptions pattern. • Because of that mismatch, the framework never reaches the worker entrypoint correctly, so you see no initialization/job logs even though the container is running. LiveKit Cloud may show a worker record from infrastructure-level signals, but the actual worker job process never joins the room. Two ways to fix this Choose one of the following depending on which pattern you want to use: 1) Keep WorkerOptions (minimal change) • Change your Docker CMD to run the script without the
start
subcommand so the WorkerOptions flow is used: CMD ["python", "interview_agent.py"] • This runs the CLI in worker mode and will allow your
entrypoint
to be used to accept jobs and join rooms. 2) Switch to AgentServer / start mode (code change) • If you prefer using
python ... start
, refactor your code to use the AgentServer pattern instead of WorkerOptions (use the AgentServer constructor and decorate RTC handlers per the AgentServer pattern). • This requires changing how you register the entrypoints and job handlers to match server/start semantics. Extra checks to run after the change • Verify the agent process actually starts inside the container: exec in and run
ps aux
(or
pgrep -a python
) and confirm the command-line matches the Docker CMD you expect. • Increase logging temporarily: run with DEBUG or add at top of `interview_agent.py`: import logging logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger("agent") logger.info("Agent process started") • Confirm required env vars still present inside container (
LIVEKIT_URL
,
LIVEKIT_API_KEY
,
LIVEKIT_API_SECRET
,
OPENAI_API_KEY
). • After changing CMD to
["python","interview_agent.py"]
, watch logs (
docker logs
/
kubectl logs
) for the
Starting interview agent job:
log and
AGENT_READY
or session start logs. If you want, I can: • Provide the exact Dockerfile snippet to replace the CMD. • Walk you through a minimal AgentServer refactor if you prefer to keep
start
. • Look at logs you get after switching CMD to confirm the worker started and accepted a job. Which option would you like to try first — change the Docker CMD to run without
start
, or convert your code to the AgentServer pattern? Sources: Server startup modes | LiveKit Documentation | Server options | LiveKit Documentation
f
what is the right way to do it? worker pattern or agent server pattern?
t
Thanks — great question. I’m replying about whether you should use the worker pattern (what you currently have) or the agent server pattern. Short answer: Use the agent server pattern (AgentServer + @server.rtc_session()). It’s the recommended, production-ready approach. Why the agent server pattern is the right choice • It’s designed for production deployments: automatic job dispatch, lifecycle management, graceful shutdown, and better observability (logs, job lifecycle events). • It integrates with LiveKit’s orchestration and load balancing so agents reliably accept jobs and join rooms. • Examples and quickstarts use this pattern, so it’s easiest to maintain and scale. When the worker pattern can make sense • It’s mainly a lower-level/legacy approach or for very custom dispatch logic. • If you need custom worker pooling or experimental behavior you can use it, but you’ll need to manage more details yourself. How to migrate (high-level) 1. Replace your WorkerOptions/agents.run_app code with an AgentServer and a decorated entrypoint: ◦ Create the server:
server = AgentServer()
◦ Use
@server.rtc_session(agent_name="...")
on your entrypoint coroutine. ◦ At the bottom:
if __name__ == "__main__": cli.run_app(server)
1. Keep your existing entrypoint logic (ctx.connect, session start, event listeners). The JobContext APIs remain the same inside the entrypoint. 2. Keep running the container with
CMD ["python", "interview_agent.py", "start"]
(the
start
subcommand is the correct CLI for the AgentServer pattern). Quick example (skeleton)
Copy code
from livekit.agents import AgentServer, cli, JobContext

server = AgentServer()

@server.rtc_session(agent_name="interview-agent")
async def entrypoint(ctx: JobContext):
    # your existing logic: ctx.connect(), session.start(), etc.
    ...
    
if __name__ == "__main__":
    cli.run_app(server)
Post-migration checks • Ensure
LIVEKIT_URL
,
LIVEKIT_API_KEY
,
LIVEKIT_API_SECRET
, and
OPENAI_API_KEY
are present in the container environment. • Run with DEBUG logging once to confirm the server registers and you see job/room join logs. • Verify the container CMD is
["python", "interview_agent.py", "start"]
so the CLI runs in server mode. If you’d like, I can: • Provide a drop-in refactor of your current main/entrypoint into the AgentServer pattern. • Show the exact Dockerfile CMD change you should make if you decide to stay on the worker pattern instead. Which would you prefer — I can prepare the AgentServer refactor for you now. Sources: Job lifecycle | LiveKit Documentation | An open source stack for real-time multimodal AI | An open source stack for real-time multimodal AI | Simple Call Answering Agent | LiveKit Documentation | Repeater | LiveKit Documentation
f
please update dockerfile and interview_agent.py. or highlight changes
t
I don't have the answer you're looking for. You could also try asking your question: • in one of the other Slack channels or • to https://deepwiki.com/livekit/livekit_composite which is trained on all LiveKit source code If you find the answer, please post it here to help others!
f
Provide a drop-in refactor of your current main/entrypoint into the AgentServer pattern.