# Single-node kody-celld for a NAS, home server, or one VPS. # # docker compose up -d # docker compose exec kody cat /data/kody.env # admin token + master key # # Durable state (users, packages, secrets, jobs) lives in the `kody-data` # volume. Back it up. `up` builds the image from this checkout when it is not # present locally; `docker compose pull` fetches the prebuilt multi-arch image # from ghcr.io instead. To upgrade: `docker compose pull && docker compose up -d` # (or `git pull && docker compose up -d --build`); state and generated operator # values are kept. # # Copy .env.example to .env to set KODY_PUBLIC_URL (the URL people will use, # e.g. https://kody.home.example.com behind your reverse proxy) or to pin your # own KODY_ADMIN_TOKEN / KODY_MASTER_KEY instead of the generated ones. services: kody: build: . image: ${KODY_IMAGE:-ghcr.io/kody-hq/kody-celld:latest} command: ['single'] init: true restart: unless-stopped ports: - '${KODY_PORT:-8080}:8080' environment: KODY_PUBLIC_URL: ${KODY_PUBLIC_URL:-http://localhost:8080} KODY_ADMIN_TOKEN: ${KODY_ADMIN_TOKEN:-} KODY_MASTER_KEY: ${KODY_MASTER_KEY:-} KODY_MASTER_KEY_PREVIOUS: ${KODY_MASTER_KEY_PREVIOUS:-} KODY_PRIVATE_HOSTS: ${KODY_PRIVATE_HOSTS:-} KODY_APP_BASE_URL: ${KODY_APP_BASE_URL:-} # Limits and quota defaults (docs/operations.md); empty = built-in default. KODY_EXECUTE_TIMEOUT_MS: ${KODY_EXECUTE_TIMEOUT_MS:-} KODY_RUN_RETENTION_COUNT: ${KODY_RUN_RETENTION_COUNT:-} KODY_RUN_RETENTION_DAYS: ${KODY_RUN_RETENTION_DAYS:-} KODY_RUN_LOG_LIMIT: ${KODY_RUN_LOG_LIMIT:-} KODY_RESPONSE_LIMIT_BYTES: ${KODY_RESPONSE_LIMIT_BYTES:-} KODY_AUDIT_RETENTION_COUNT: ${KODY_AUDIT_RETENTION_COUNT:-} KODY_QUOTA_RUNS_PER_DAY: ${KODY_QUOTA_RUNS_PER_DAY:-} KODY_QUOTA_EXECUTE_MS_PER_DAY: ${KODY_QUOTA_EXECUTE_MS_PER_DAY:-} KODY_QUOTA_PACKAGES: ${KODY_QUOTA_PACKAGES:-} KODY_QUOTA_SECRETS: ${KODY_QUOTA_SECRETS:-} KODY_QUOTA_JOBS: ${KODY_QUOTA_JOBS:-} # AI, memories and semantic search (docs/ai.md); empty = off / lexical only. KODY_AI_PROVIDER: ${KODY_AI_PROVIDER:-} KODY_AI_BASE_URL: ${KODY_AI_BASE_URL:-} KODY_AI_API_KEY: ${KODY_AI_API_KEY:-} KODY_AI_CHAT_MODEL: ${KODY_AI_CHAT_MODEL:-} KODY_AI_EMBED_PROVIDER: ${KODY_AI_EMBED_PROVIDER:-} KODY_AI_EMBED_BASE_URL: ${KODY_AI_EMBED_BASE_URL:-} KODY_AI_EMBED_API_KEY: ${KODY_AI_EMBED_API_KEY:-} KODY_AI_EMBED_MODEL: ${KODY_AI_EMBED_MODEL:-} KODY_AI_EMBED_DIMENSIONS: ${KODY_AI_EMBED_DIMENSIONS:-} KODY_AI_TIMEOUT_MS: ${KODY_AI_TIMEOUT_MS:-} KODY_SEARCH_RERANK: ${KODY_SEARCH_RERANK:-} KODY_VECTOR_PROVIDER: ${KODY_VECTOR_PROVIDER:-} KODY_QDRANT_URL: ${KODY_QDRANT_URL:-} KODY_QDRANT_API_KEY: ${KODY_QDRANT_API_KEY:-} KODY_QDRANT_COLLECTION: ${KODY_QDRANT_COLLECTION:-} # Blob storage (docs/blobs.md); empty = celld R2 binding on the fleet bucket. KODY_BLOB_PROVIDER: ${KODY_BLOB_PROVIDER:-} KODY_BLOB_S3_ENDPOINT: ${KODY_BLOB_S3_ENDPOINT:-} KODY_BLOB_S3_BUCKET: ${KODY_BLOB_S3_BUCKET:-} KODY_BLOB_S3_REGION: ${KODY_BLOB_S3_REGION:-} KODY_BLOB_S3_ACCESS_KEY_ID: ${KODY_BLOB_S3_ACCESS_KEY_ID:-} KODY_BLOB_S3_SECRET_ACCESS_KEY: ${KODY_BLOB_S3_SECRET_ACCESS_KEY:-} KODY_BLOB_S3_PREFIX: ${KODY_BLOB_S3_PREFIX:-} KODY_BLOB_S3_FORCE_PATH_STYLE: ${KODY_BLOB_S3_FORCE_PATH_STYLE:-} KODY_BLOB_MAX_BYTES: ${KODY_BLOB_MAX_BYTES:-} KODY_BLOB_URL_TTL_SECONDS: ${KODY_BLOB_URL_TTL_SECONDS:-} KODY_QUOTA_BLOBS: ${KODY_QUOTA_BLOBS:-} KODY_QUOTA_BLOB_BYTES: ${KODY_QUOTA_BLOB_BYTES:-} KODY_MCP_CONTENT_LIMIT_BYTES: ${KODY_MCP_CONTENT_LIMIT_BYTES:-} # External MCP servers (docs/mcp-servers.md); empty = public https only, Cloudflare DoH, 30 s calls, no count limit. KODY_MCP_CALL_TIMEOUT_MS: ${KODY_MCP_CALL_TIMEOUT_MS:-} KODY_DNS_RESOLVER_URL: ${KODY_DNS_RESOLVER_URL:-} KODY_QUOTA_MCP_SERVERS: ${KODY_QUOTA_MCP_SERVERS:-} # Browser rendering (docs/browser.md); empty = off. compose.browser.yaml adds browserless. KODY_BROWSER_PROVIDER: ${KODY_BROWSER_PROVIDER:-} KODY_BROWSER_URL: ${KODY_BROWSER_URL:-} KODY_BROWSER_TOKEN: ${KODY_BROWSER_TOKEN:-} KODY_BROWSER_TIMEOUT_MS: ${KODY_BROWSER_TIMEOUT_MS:-} # npm imports + package installs (docs/packages.md); empty = esm.sh, durable cache on. compose.esm.yaml adds a self-hosted CDN. KODY_NPM_IMPORTS: ${KODY_NPM_IMPORTS:-} KODY_ESM_CDN_URL: ${KODY_ESM_CDN_URL:-} KODY_NPM_CACHE_MAX_MB: ${KODY_NPM_CACHE_MAX_MB:-} KODY_NPM_CACHE_TTL_DAYS: ${KODY_NPM_CACHE_TTL_DAYS:-} # Webhooks + email (docs/webhooks.md, docs/email.md); empty = email off. compose.mail.yaml adds the SMTP bridge. KODY_WEBHOOK_MAX_BODY_BYTES: ${KODY_WEBHOOK_MAX_BODY_BYTES:-} KODY_EMAIL_DOMAIN: ${KODY_EMAIL_DOMAIN:-} KODY_EMAIL_INBOUND_TOKEN: ${KODY_EMAIL_INBOUND_TOKEN:-} KODY_EMAIL_OUTBOUND_PROVIDER: ${KODY_EMAIL_OUTBOUND_PROVIDER:-} KODY_EMAIL_OUTBOUND_URL: ${KODY_EMAIL_OUTBOUND_URL:-} KODY_EMAIL_OUTBOUND_TOKEN: ${KODY_EMAIL_OUTBOUND_TOKEN:-} KODY_EMAIL_FROM_NAME: ${KODY_EMAIL_FROM_NAME:-} KODY_EMAIL_TIMEOUT_MS: ${KODY_EMAIL_TIMEOUT_MS:-} KODY_EMAIL_MAX_BYTES: ${KODY_EMAIL_MAX_BYTES:-} KODY_QUOTA_EMAIL_MESSAGES: ${KODY_QUOTA_EMAIL_MESSAGES:-} KODY_QUOTA_EMAIL_SENDS_PER_DAY: ${KODY_QUOTA_EMAIL_SENDS_PER_DAY:-} KODY_QUOTA_EMAIL_RECEIVES_PER_DAY: ${KODY_QUOTA_EMAIL_RECEIVES_PER_DAY:-} KODY_SECRET_PROVIDER_CACHE_SECONDS: ${KODY_SECRET_PROVIDER_CACHE_SECONDS:-} KODY_SECRET_PROVIDER_TIMEOUT_MS: ${KODY_SECRET_PROVIDER_TIMEOUT_MS:-} # celld LTX epoch GC grace (docs/operations.md). Empty = single-mode # default of 86400 (1 day). Set 0 to disable deletion of superseded epochs. CELLD_LTX_RETENTION_SECS: ${CELLD_LTX_RETENTION_SECS:-} CELLD_DEV_FLAGS: ${CELLD_DEV_FLAGS:-} volumes: - kody-data:/data extra_hosts: - 'host.docker.internal:host-gateway' volumes: kody-data: