Hosting the Agent SDK
View SourceOfficial Documentation: This guide is based on the official Claude Agent SDK documentation. Examples are adapted for Elixir.
Deploy ClaudeCode sessions in production using OTP supervision trees and Elixir releases.
Supervision Trees
Use ClaudeCode.Supervisor to manage multiple agent sessions with automatic restart and fault isolation:
# lib/my_app/application.ex
defmodule MyApp.Application do
use Application
def start(_type, _args) do
children = [
MyAppWeb.Endpoint,
{ClaudeCode.Supervisor, [
[name: :assistant, system_prompt: "General-purpose helper"],
[name: :code_reviewer, system_prompt: "You review Elixir code for quality"],
[name: :test_writer, system_prompt: "You write ExUnit tests"]
]}
]
Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor)
end
endUse named sessions from anywhere in your app:
# Controller
def chat(conn, %{"message" => message}) do
response =
:assistant
|> ClaudeCode.stream(message)
|> ClaudeCode.Stream.text_content()
|> Enum.join()
json(conn, %{response: response})
end
# GenServer
def handle_call({:review, code}, _from, state) do
result =
:code_reviewer
|> ClaudeCode.stream("Review: #{code}")
|> ClaudeCode.Stream.text_content()
|> Enum.join()
{:reply, {:ok, result}, state}
endSupervisor Options
{ClaudeCode.Supervisor, session_configs,
name: MyApp.ClaudeSupervisor,
max_restarts: 5,
max_seconds: 10
}Per-Session Configuration
{ClaudeCode.Supervisor, [
# Fast for simple queries
[name: :quick, timeout: 30_000, max_turns: 5],
# Deep analysis
[name: :analyzer, timeout: 600_000, max_turns: 50],
# Code assistant with file access
[name: :coder,
allowed_tools: ["View", "Edit", "Bash(git:*)"],
add_dir: ["/app/lib"]]
]}Dynamic Sessions
Add and remove sessions at runtime:
# Start with base sessions
{ClaudeCode.Supervisor, [
[name: :shared_assistant]
]}
# Add user-specific session
def create_user_session(user_id) do
ClaudeCode.Supervisor.start_session(ClaudeCode.Supervisor, [
name: {:user, user_id},
system_prompt: "You are helping user #{user_id}"
])
end
# Query user session
{:user, user_id} |> ClaudeCode.stream(message) |> Stream.run()
# Clean up
def cleanup_user_session(user_id) do
ClaudeCode.Supervisor.terminate_session(ClaudeCode.Supervisor, {:user, user_id})
endRegistry-Based Sessions
For advanced session discovery:
children = [
{Registry, keys: :unique, name: MyApp.SessionRegistry},
{ClaudeCode.Supervisor, [
[name: {:via, Registry, {MyApp.SessionRegistry, :primary}}]
]}
]
# Access via registry
session = {:via, Registry, {MyApp.SessionRegistry, :primary}}
session |> ClaudeCode.stream("Hello") |> Stream.run()Fault Tolerance
Automatic Restart
Sessions restart transparently after crashes:
:assistant |> ClaudeCode.stream("Complex task") |> Stream.run()
# Even if :assistant crashes, it restarts automatically
:assistant |> ClaudeCode.stream("Another task") |> Stream.run()Note: Conversation history is lost on restart.
Independent Failure
One session crash doesn't affect others:
# If :code_reviewer crashes, :test_writer continues working
try do
:code_reviewer |> ClaudeCode.stream(bad_input) |> Stream.run()
catch
:error, _ -> :crashed
end
# Still works
:test_writer
|> ClaudeCode.stream("Write tests")
|> ClaudeCode.Stream.text_content()
|> Enum.join()Management API
# List sessions
ClaudeCode.Supervisor.list_sessions(ClaudeCode.Supervisor)
# Count sessions
ClaudeCode.Supervisor.count_sessions(ClaudeCode.Supervisor)
# Restart session (clears history)
ClaudeCode.Supervisor.restart_session(ClaudeCode.Supervisor, :assistant)
# Add session at runtime
ClaudeCode.Supervisor.start_session(ClaudeCode.Supervisor, [
name: :temporary,
system_prompt: "Temporary helper"
])
# Remove session
ClaudeCode.Supervisor.terminate_session(ClaudeCode.Supervisor, :temporary)Elixir Releases
For deploying with mix release, ensure the CLI binary is included:
# config/runtime.exs
config :claude_code,
api_key: System.get_env("ANTHROPIC_API_KEY"),
model: System.get_env("CLAUDE_MODEL", "sonnet")The default cli_path: :bundled mode uses the CLI binary in priv/bin/, which is automatically included in releases. Pre-install it during your release build:
mix claude_code.install
For alternative setups, see the CLI Configuration section of ClaudeCode.Options.
Resource Considerations
Each ClaudeCode session runs a separate CLI subprocess:
| Resource | Per Session |
|---|---|
| OS process | 1 Node.js process |
| Memory | ~50-100 MB |
| File descriptors | 3 (stdin, stdout, stderr) |
| Ports | 1 Erlang port |
Plan accordingly when running multiple concurrent sessions.
Scaling Patterns
Task Pool
For handling many concurrent requests with a fixed number of sessions:
defmodule MyApp.AgentPool do
def query(prompt) do
session = select_session()
session
|> ClaudeCode.stream(prompt)
|> ClaudeCode.Stream.final_text()
end
defp select_session do
sessions = ClaudeCode.Supervisor.list_sessions(MyApp.ClaudeSupervisor)
{_id, pid, _, _} = Enum.random(sessions)
pid
end
endPer-Request Sessions
For isolation between requests:
defmodule MyApp.IsolatedAgent do
def run(prompt, opts \\ []) do
{:ok, session} = ClaudeCode.start_link(opts)
try do
session
|> ClaudeCode.stream(prompt)
|> ClaudeCode.Stream.collect()
after
ClaudeCode.stop(session)
end
end
endHealth Monitoring
defmodule MyApp.HealthCheck do
def ai_status do
sessions = ClaudeCode.Supervisor.list_sessions(ClaudeCode.Supervisor)
%{
total: length(sessions),
active: Enum.count(sessions, fn {_, pid, _, _} -> Process.alive?(pid) end)
}
end
def test_connectivity do
try do
:assistant
|> ClaudeCode.stream("ping", timeout: 10_000)
|> Stream.run()
:healthy
catch
error -> {:unhealthy, error}
end
end
endTroubleshooting
Session not found:
ClaudeCode.Supervisor.list_sessions(ClaudeCode.Supervisor)Session keeps crashing:
valid_config = [
name: :test,
api_key: System.fetch_env!("ANTHROPIC_API_KEY"),
timeout: 60_000
]Next Steps
- Secure Deployment - Security hardening
- Sessions - Session management details
- Cost Tracking - Monitor API costs