Summary
The self-hosting docs (SelfHosting.md) mention AiPlanner__AnthropicApiKey as the only
configuration point for the AI planner, which implies Anthropic is the only usable provider.
It's actually possible to run the planner completely free using OpenRouter's free-tier
models, routed through a small LiteLLM proxy. Wanted to document this for others hitting the
same "I don't want to pay for Claude" question.
Why this works
LiftLog.Api constructs the Anthropic client as new AnthropicClient { ApiKey = apiKey }
(see RegistrationHelpers.cs). Since BaseUrl is never set explicitly, the underlying SDK
falls back to the ANTHROPIC_BASE_URL environment variable if present. That means you can
point the server at any endpoint that speaks Anthropic's /v1/messages format. This includes a
self-hosted LiteLLM proxy, which can translate that format to OpenRouter (or any other
provider) under the hood.
Setup
docker-compose.yml (added service alongside the LiftLog API container):
name: liftlog
services:
liftlog:
image: ghcr.io/liammorrow/liftlog:api
container_name: liftlog
restart: unless-stopped
environment:
Database__Provider: Sqlite
Database__ConnectionString: "Data Source=/var/lib/liftlog/liftlog.db"
Auth__ApiKey__Value: ${LIFTLOG_API_KEY}
AiPlanner__AnthropicApiKey: ${LITELLM_MASTER_KEY}
ANTHROPIC_BASE_URL: "http://litellm:4000"
volumes:
- liftlog_data:/var/lib/liftlog
networks:
- proxy
- default
depends_on:
- litellm
labels:
- "traefik.enable=true"
- "traefik.http.routers.liftlog-rtr.entrypoints=websecure"
- "traefik.docker.network=proxy"
- "traefik.http.routers.liftlog-rtr.rule=Host(`liftlog.example.com`)"
- "traefik.http.routers.liftlog-rtr.tls=true"
- "traefik.http.routers.liftlog-rtr.service=liftlog-svc"
- "traefik.http.services.liftlog-svc.loadbalancer.server.port=8080"
- "com.centurylinklabs.watchtower.enable=true"
litellm:
image: ghcr.io/berriai/litellm:main-stable
container_name: liftlog-litellm
restart: unless-stopped
environment:
LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY}
volumes:
- ~/docker/liftlog/litellm-config.yaml:/app/config.yaml:ro
command: ["--config", "/app/config.yaml", "--port", "4000"]
networks:
- default
volumes:
liftlog_data:
networks:
default:
proxy:
external: true
litellm-config.yaml:
model_list:
- model_name: claude-sonnet-4-6 # must match AiPlanner__AnthropicModelId (default)
litellm_params:
model: openrouter/nvidia/nemotron-3-ultra-550b-a55b:free
api_key: os.environ/OPENROUTER_API_KEY
- model_name: claude-sonnet-4-6-fallback-1
litellm_params:
model: openrouter/nvidia/nemotron-3-super-120b-a12b:free
api_key: os.environ/OPENROUTER_API_KEY
router_settings:
fallbacks:
- claude-sonnet-4-6: ["claude-sonnet-4-6-fallback-1"]
cooldown_time: 30
No code changes to LiftLog itself needed. Just the env var and the proxy container.
Caveats worth documenting
- OpenRouter's free models are rate-limited (typically ~20 req/min, ~200/day) and occasionally
return provider_unavailable under load. So LiteLLM's fallbacks config handles that
gracefully by trying the next model.
- Not all free models honour the planner's tool-calling JSON schema as reliably as Claude does;
bigger reasoning models (e.g. Nemotron 3 Ultra) do better than smaller ones, but occasional
malformed plans are more likely than with the real Anthropic API.
- This is obviously best-effort/community knowledge, not something the project needs to support
officially. I am just flagging it in case it's worth a line in SelfHosting.md or Backends.md for
discoverability, since "can I avoid the Anthropic API cost" seems like a common question for
self-hosters.
Happy to open a PR against the docs if that's welcome instead of just an issue.
Summary
The self-hosting docs (SelfHosting.md) mention
AiPlanner__AnthropicApiKeyas the onlyconfiguration point for the AI planner, which implies Anthropic is the only usable provider.
It's actually possible to run the planner completely free using OpenRouter's free-tier
models, routed through a small LiteLLM proxy. Wanted to document this for others hitting the
same "I don't want to pay for Claude" question.
Why this works
LiftLog.Apiconstructs the Anthropic client asnew AnthropicClient { ApiKey = apiKey }(see
RegistrationHelpers.cs). SinceBaseUrlis never set explicitly, the underlying SDKfalls back to the
ANTHROPIC_BASE_URLenvironment variable if present. That means you canpoint the server at any endpoint that speaks Anthropic's
/v1/messagesformat. This includes aself-hosted LiteLLM proxy, which can translate that format to OpenRouter (or any other
provider) under the hood.
Setup
docker-compose.yml (added service alongside the LiftLog API container):
litellm-config.yaml:
No code changes to LiftLog itself needed. Just the env var and the proxy container.
Caveats worth documenting
return
provider_unavailableunder load. So LiteLLM'sfallbacksconfig handles thatgracefully by trying the next model.
bigger reasoning models (e.g. Nemotron 3 Ultra) do better than smaller ones, but occasional
malformed plans are more likely than with the real Anthropic API.
officially. I am just flagging it in case it's worth a line in SelfHosting.md or Backends.md for
discoverability, since "can I avoid the Anthropic API cost" seems like a common question for
self-hosters.
Happy to open a PR against the docs if that's welcome instead of just an issue.