Self-Hosting

Run your own tela. It's a Go + PostgreSQL + React stack shipped as a Docker Compose deployment behind a Caddy proxy, and you need only Docker. Your instance's root URL is the app itself: signed-out visitors land on the login page.

tela is AGPL-3.0; the "tela" name and logo are reserved trademarks.

Quick start

git clone https://github.com/zcag/tela && cd tela
make setup            # writes deploy/.env from the example with generated secrets
$EDITOR deploy/.env   # set TELA_PUBLIC_BASE_URL, admin creds, SMTP
make up               # build + start
make logs             # the bootstrap admin password prints here once

The stack publishes host port 8780 (Caddy → app). The first admin comes from TELA_ADMIN_USERNAME/PASSWORD/EMAIL on first boot — or, if you leave TELA_ADMIN_PASSWORD unset, from the /setup web wizard the fresh instance lands on.

Configuration

Everything is env-driven (deploy/.env; every variable is documented in .env.example). The ones that matter:

Variable Notes
TELA_PG_PASSWORD No default — required. make setup generates it.
TELA_PUBLIC_BASE_URL Must match how users reach the instance — it drives emailed links and the cookie Secure flag.
TELA_SHARE_SECRET, TELA_API_KEY_SECRET HMAC keys. Unset → generated and persisted on first boot (stable across restarts); set them to pin/rotate from the environment. Rotating them invalidates outstanding share cookies / PATs.
TELA_SMTP_* Required for a usable multi-user instance (see Email).
TELA_DATABASE_URL Auto-built from TELA_PG_* in this compose stack — set it only when pointing at an external Postgres.

The backend logs its effective config at boot (config: …) — check those lines first when something's off.

TLS

The proxy defaults to plain HTTP on :80 (published as 8780), correct behind an external terminator (Cloudflare, a load balancer) — point it at :8780 and set an https:// base URL. For direct TLS, set TELA_SITE_ADDRESS=your.domain, publish 80+443 on the proxy, and Caddy auto-provisions a certificate. Direct TLS is also the prerequisite for org [[Custom domains]] (on-demand cert issuance can't fire behind an external terminator).

[!WARNING] The login cookie is Secure only when TELA_PUBLIC_BASE_URL is https://. If it's https but you serve plain HTTP, browsers drop the cookie and login silently fails. Keep the scheme matched to how users connect.

Email

With TELA_SMTP_HOST unset, tela logs verify/reset links instead of sending them — fine for a single admin, but open self-registration is unusable without SMTP (confirmation links only hit the log). Configure TELA_SMTP_* for any multi-user instance; it works with any SMTP relay (e.g. Resend).

Registration policy

By default anyone can register. For a private or internal instance, close self-registration after the initial admin setup: go to Settings → Instance (as an instance admin) or call the admin API:

curl -s -X PATCH https://your.host/api/admin/settings \
  -H 'Authorization: Bearer tela_pat_…' \
  -H 'content-type: application/json' \
  -d '{"settings":{"registration_open":"false"}}'

Branding

Give your wiki its own name, logo and accent color under Settings > Instance > Branding. They show in the sidebar, the sign-in screens, tab titles, the favicon and loading screen, link previews and emails. A small "Powered by tela" credit shows whenever a name or logo is set. Clear the fields to go back to tela's look. On an org's [[Custom domains]], that org's own branding still applies.

To set it from the environment instead, these apply on first boot only (after that, Settings wins):

TELA_BRAND_NAME=Acme Wiki
TELA_BRAND_ACCENT=#0e7490
TELA_BRAND_LOGO_URL=https://example.com/logo.png

The logo is fetched once and stored in tela: PNG, JPEG, WebP or GIF, up to 2 MB.

[!NOTE] The favicon, tab title and loading screen are served by the frontend container, which asks the backend for them at backend:8080. Both compose files make that name resolve. On a host without Docker's DNS (ECS, Kubernetes, a bare VM), set TELA_BACKEND on the frontend container to an address it can reach, such as 127.0.0.1:8080 in a shared ECS task, and TELA_RESOLVER to a DNS server if that address is a hostname. Otherwise those three keep tela's defaults.

File sync (WebDAV)

tela exposes a WebDAV surface at /dav/ so you can sync pages from rclone, Obsidian, or any WebDAV client using a Personal Access Token. See [[Sync your vault (WebDAV)]] for setup steps.

[!NOTE] If you run behind Cloudflare: CF's default WAF rules block non-standard HTTP methods (PROPFIND, MKCOL, MOVE, COPY, etc.). You need to add a WAF skip rule for /dav/* on your zone, or WebDAV will fail with 403 errors from CF's edge — not from tela.

WebDAV is on by default; disable it with TELA_WEBDAV_ENABLED=0 in deploy/.env.

Semantic search & AI

Full-text search works out of the box. Semantic search ("ask your docs") needs an embedder and an LLM — both ship dark (return 503) until configured.

[!NOTE] Changing the embed model means re-embedding everything: after editing TELA_RAG_EMBED_MODEL, run … exec backend /tela reindex-all (resumable).

Reliability & failover

AI endpoints are external — if one clogs (rate-limited, slow, or down), the features that depend on it degrade. To keep them up, run the optional relief proxy: make up-relief starts a LiteLLM proxy in front of chat and embeddings, each with a primary and a relief endpoint, so traffic fails over automatically when the primary clogs. Set the endpoints in deploy/.env (the AI relief proxy block in .env.example); a relief embedder must output the same dimension (1024) as the primary.

Either way, Settings → Insights → AI endpoints & reliability (instance admin) shows each service's live status, probe latency, and whether it's behind a relief pool — the at-a-glance view of what's happening. Set TELA_GRAFANA_AI_URL to deep-link the card to a Grafana dashboard for the deeper per-endpoint failover metrics.

That status comes from a background probe that sends one real embed request (and a chat ping, when chat is configured) every 30s, so it shows up in your embedder's own logs. Set TELA_AI_PROBE_INTERVAL to a longer Go duration (e.g. 5m) to probe less often; the minimum is 5s. Set it to 0 to turn the probe off: AI then counts as available whenever it's configured, the "AI unavailable" banner no longer appears on its own when an endpoint goes down, the card shows Not monitored, and the AI health metrics at /metrics stay empty. Available from v0.11.3.

AI agents (MCP)

tela has a built-in MCP server at /api/mcp. Connect Claude, Claude Code, or any MCP client to https://your.host/api/mcp with a PAT as the bearer token. See [[Agents & MCP]] for tool reference and client setup.

Backups & upgrades

make backup                       # → ./backups/tela-<timestamp>.sql
make restore FILE=backups/...     # restore a dump

Upgrade with git pull && make up — migrations run automatically on boot (forward-only). Back up before upgrading.

Update notices. Once a day tela asks updates.telawiki.com for the latest release. When a newer one is out, instance admins see Update available in the account menu, and Settings > Instance > Updates shows the new version with a link to its release notes and upgrade steps.

The check sends the version you run and nothing else: no instance id, hostname, users or content. telawiki.com uses it to count how many instances run tela, and which versions, without keeping IP addresses (each check is counted under a hash of the address with a salt that is replaced every month). Turn it off with Check for new versions in Settings > Instance, or set TELA_UPDATE_CHECK=0 in deploy/.env to turn it off for good.

Operations