Self-Hosting Walkthrough
This walkthrough takes you from nothing to a multi-user, workspace-isolated Codumentor instance — real logins, encrypted per-user secrets, sandboxed conversations, and a working plugin set — running behind a reverse proxy. It is the connective tissue between the reference pages; each step links to the detail it summarises.
The end state mirrors the project's own codumentor-selfdev-isolated.yaml: a base config plus an isolation overlay layered with extends.
Just want to try it on your laptop? Use Setup → Getting Started instead — single user, no auth, no isolation. Come back here when you're deploying for a team.
What you'll build
- A base config (
codumentor.yaml) — models, repos, auth, core plugins. - An isolation overlay (
codumentor-isolated.yaml) thatextendsthe base and adds the bubblewrap sandbox + SCM plugins. - The instance running under systemd behind nginx, with all secrets supplied as environment variables.
Step 1 — Install
Install from source (recommended for a server) or grab the portable build. See Installation for both paths. From source:
git clone <your-codumentor-remote> /opt/codumentor && cd /opt/codumentor
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
Step 2 — Install host prerequisites for the features you want
Plugins are where most deployments get their value, and several need software on the host that pip install does not provide. Decide which features you want, then install their prerequisites using the matrix in Plugins in Production → Prerequisites at a glance.
For the isolated, plugin-rich target in this guide:
# Workspace isolation / secure push / sandboxed external agents (Linux only):
# bubblewrap with overlay support — see the isolation prerequisites page,
# it usually means building bwrap 0.8.0+ from source.
./src/codumentor/plugins/workspace_isolation/setup/check.sh # verify, then setup.sh if needed
# Common tool plugins:
sudo apt install ripgrep subversion # ripgrep + svn plugins
pip install playwright && playwright install chromium # mermaid_render + webretrieve (JS)
Full details and the AppArmor note for Ubuntu 24.04+ are in Workspace isolation prerequisites.
Step 3 — Provide secrets as environment variables
Never put secrets in the config file. Codumentor reads ${VAR} references at load time. At minimum, for this guide:
# Encryption + session signing — generate once, keep stable for the deployment's life.
export CODUMENTOR_JWT_SECRET="$(python -c 'import secrets; print(secrets.token_urlsafe(48))')"
# Local login passwords:
export CODUMENTOR_ADMIN_PASSWORD='…'
export CODUMENTOR_DEV_PASSWORD='…'
# Model / service keys, as needed by your config:
export OPENAI_API_KEY='…' # chat/embeddings and/or speech
export TAVILY_API_KEY='…' # websearch, if enabled
In production these live in a systemd EnvironmentFile or a secrets manager — see Security → Secrets Management. CODUMENTOR_JWT_SECRET is special: without it, sessions reset on every restart and the per-user secret / model-profile / OAuth stores are written unencrypted — see jwt_secret.
Step 4 — Write the base config
Create /opt/codumentor/codumentor.yaml. Point models at your LLM endpoint (any OpenAI-compatible API; for a self-hosted model see LLM Server) and list your repos:
repos:
- url: https://gitea.example.com/org/main-app.git
options:
platform: gitea # enables correct permalink generation
- ssh://git@gitea.example.com:2244/org/sandbox.git
repos_dir: "../repos"
models:
agent: "your-chat-model"
agent_base_url: "https://your-llm-endpoint/v1/"
agent_api_key: "${OPENAI_API_KEY}"
embedding: "your-embedding-model"
embedding_base_url: "https://your-llm-endpoint/v1/"
embedding_api_key: "${OPENAI_API_KEY}"
vector_db:
path: "./data/vector_db"
api:
host: "127.0.0.1" # behind the reverse proxy; see Step 7
port: 2638
See Configuration for every option.
Step 5 — Add real logins (local auth)
For a small team without Gitea/LDAP, the local provider defines users in config with env-injected passwords. Add to the base config:
auth:
provider: local
jwt_secret: "${CODUMENTOR_JWT_SECRET}"
local:
users:
- username: admin
password: "${CODUMENTOR_ADMIN_PASSWORD}"
email: admin@example.com
roles: ["admin", "user", "power_user"]
- username: dev
password: "${CODUMENTOR_DEV_PASSWORD}"
roles: ["user"]
Prefer pbkdf2_sha256$ hashes if you'd rather commit the config — see Authentication → Local. For Gitea or LDAP/AD instead, see the Authentication guide.
Step 6 — Enable plugins
Add a plugins: list to the base config. Start with a useful core and grow from the full catalog — remember each plugin's host prerequisites from Step 2.
plugins:
- { module: codumentor.plugins.user_secrets, class: UserSecretsPlugin, args: { enabled: true } }
- { module: codumentor.plugins.ripgrep, class: RipgrepPlugin }
- { module: codumentor.plugins.explore, class: ExplorePlugin, args: { enabled: true } }
- { module: codumentor.plugins.context_summarization, class: ContextSummarizationPlugin, priority: 10, args: { max_context_tokens: 170000 } }
- { module: codumentor.plugins.configuration_assistant, class: ConfigurationAssistantPlugin, args: { enabled: true } }
# …add websearch, database, redmine, the Office editors, read_aloud (+ a speech: block), etc.
user_secrets is what makes per-user credentials work (and relies on jwt_secret from Step 3). For the read-aloud / voice features, add a top-level speech: block.
Step 7 — Layer the isolation overlay with extends
Rather than editing the base, keep isolation in a second file that inherits it. extends loads the parent first; plugins_overrides deep-merges by name and appends the rest, so you don't restate the base plugin list. (This is exactly how codumentor-selfdev-isolated.yaml is built.) See Config inheritance with extends.
Create /opt/codumentor/codumentor-isolated.yaml:
extends: codumentor.yaml
plugins_overrides:
- module: codumentor.plugins.workspace_isolation
class: WorkspaceIsolationPlugin
priority: 5
args:
enabled: true
persistent_home: true
subagent_isolation: shared
snapshots: { upper_dir_ttl: 2592000, cleanup_interval: 3600, min_free_gb: 5 }
sandbox: { unshare_net: true }
- { module: codumentor.plugins.scm_push, class: ScmPushPlugin, priority: 25, dependencies: ["workspace_isolation", "user_secrets"], args: { enabled: true } }
# add svn / branch_switching here too if you use them
This is the file you run in production. (You can run the plain base config without isolation any time — useful for comparison or on a host without bubblewrap.)
Step 8 — Ingest, run, and put a proxy in front
# Index the repos once (and on a schedule / via webhooks later):
.venv/bin/python -m codumentor -c codumentor-isolated.yaml ingest
# Run the API:
.venv/bin/python -m codumentor -c codumentor-isolated.yaml api --host 127.0.0.1 --port 2638
For the systemd unit, nginx config (the SSE proxy headers are mandatory), data directories, and backups, follow Deployment. The API must stay on 127.0.0.1 with TLS terminated at the proxy.
Step 9 — Verify
- Browse to your proxied URL and log in as
adminwithCODUMENTOR_ADMIN_PASSWORD. - Start a conversation and confirm answers cite your indexed repos.
- Confirm isolation is active: a shell/edit tool runs inside the sandbox (changes land in the overlay, not your real checkout). If startup logged a bubblewrap/overlay error, revisit Workspace isolation prerequisites.
- Check the startup log has no
auth.jwt_secret … not setERROR — if it does, your env var didn't reach the process and secrets are unencrypted. - Open Settings → User Secrets and add a credential a plugin needs (e.g. a Redmine API key); confirm it saves.
Step 10 — Harden
Run through the Security checklist before opening the instance to users: TLS, loopback bind, shell_always_allow: false, restricted data-dir permissions, log rotation, backups, and trusted plugins only.
See also
- Installation · Configuration · Authentication
- Plugins in Production · Deployment · Security
- Setup → Plugins — the full plugin catalog