Repository navigation
Guides Ops Pm2
pm2 process manager for VPS deployments.
bun install -g pm2
bun install -g @cuongtran001/kannaPort and password are CLI flags, so they go after --:
pm2 start --name kanna kanna -- --port 3210 --password changeme --no-open
pm2 save
pm2 startupOr keep the settings in an ecosystem.config.cjs, in a directory of its own so the
deploy config is versioned apart from any source checkout:
module.exports = {
apps: [
{
name: "kanna",
script: "/home/<user>/.bun/bin/kanna",
args: ["--no-open", "--port", "3210", "--password", "<UI_PASSWORD>"],
interpreter: "none",
exec_mode: "fork",
autorestart: true,
watch: false,
},
],
}Do not set NODE_ENV. Kanna's own mode comes from KANNA_RUNTIME_PROFILE
(production unless it is dev), and Kanna never passes its NODE_ENV on to the
agents, terminals and loop checks it starts for a project.
The pm2 daemon keeps the environment of the shell that first started it, and every
process it launches inherits that environment. pm2's env: block can only add or
override keys, never remove them. If the daemon was first started from inside a
Claude Code session, it carries CLAUDE_CODE_SESSION_ID, CLAUDE_CODE_EXECPATH,
AI_AGENT and similar variables. Kanna removes only CLAUDECODE before it starts
Claude, so the rest reach the agent and can override the OAuth token from the pool,
which shows up as:
Failed to authenticate. API Error: 401 Invalid authentication credentials
Start the daemon itself under env -i so it comes up clean whatever shell you are
in:
pm2 delete all 2>/dev/null
pm2 kill
rm -f ~/.pm2/dump.pm2
env -i \
HOME=$HOME \
PATH=$HOME/.bun/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin \
SHELL=/bin/bash \
LANG=en_US.UTF-8 \
USER=$USER \
LOGNAME=$USER \
PM2_HOME=$HOME/.pm2 \
$HOME/.bun/bin/pm2 start /path/to/ecosystem.config.cjs
pm2 saveCheck that nothing leaked in. This must print nothing:
pm2 env 0 | grep -iE '^(CLAUDE|ANTHROPIC|AI_AGENT)'Two strategies, and the default is the one you want for a normal npm install.
-
supervisor(default). The self-update button installs the new version from npm and exits; pm2 restarts the process on the new code. Nothing to configure — this is what runs unless you say otherwise. -
KANNA_RELOADER=pm2. For running Kanna from a git checkout rather than the npm package: it pulls, rebuilds, and runspm2 reload. It requiresKANNA_REPO_DIRpointing at that checkout and throws at startup without it — the failure is loud, not silent.
Kanna does not sniff for pm2. Setting neither variable is correct under pm2.
To update by hand instead, install the new version and restart; pm2 keeps the same environment and arguments:
bun install -g @cuongtran001/kanna@latest
pm2 restart kannaCaution
max_memory_restart above 2 GB is silently clamped
pm2 7.0.3 clamps the value at 2³¹ bytes: both "3G" and "4G" resolve to
2147483648. Raising the ceiling past 2 GB is not available, so if Kanna is
being restarted for memory, the lever is reducing RSS rather than raising the
limit. KANNA_MEMLOG_MS and the SIGUSR2 heap snapshot
(Self-host basics)
are how you find out what is holding it.
The client bundle is content-hashed, so an upgrade deletes the previous chunks. A tab loaded before the restart still runs the old code, and the first lazily loaded feature it opens (a Mermaid diagram, syntax highlighting) asks for a chunk that no longer exists. Reload the tab; the deploy is fine. A Mermaid diagram in that state says "This diagram needs the latest version of the app" and offers a Reload button, and a later attempt in the same tab retries instead of staying broken.
To confirm the served build is consistent after a deploy:
# The entry chunk index.html references must exist
curl -s http://127.0.0.1:3210/ | grep -o '/assets/index-[^"]*\.js'
# A missing asset must be a 404, never text/html
curl -sI http://127.0.0.1:3210/assets/does-not-exist.js | head -2| Command | Effect |
|---|---|
pm2 status kanna |
Process state |
pm2 logs kanna --lines 50 |
Tail stdout and stderr |
pm2 stop kanna |
Stop |
pm2 restart kanna |
Restart with the same environment |
pm2 reload kanna --update-env |
Restart and re-read the env: block |
pm2 save |
Save the process list for pm2 resurrect after a reboot |
-
pm2 env 0 | grep CLAUDEmust print nothing. If it does, restart the daemon underenv -ias above. -
In Settings → Claude accounts, check that the OAuth pool has at least one
active(notlimited) token. -
Test a token against the
claudebinary Kanna ships with. For abun install -ginstall it is in~/.bun/install/global/node_modules/@anthropic-ai/claude-agent-sdk-<platform>/, where<platform>is for examplelinux-x64ordarwin-arm64:CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-... \ ~/.bun/install/global/node_modules/@anthropic-ai/claude-agent-sdk-linux-x64/claude \ -p "hi" --model claude-haiku-4-5-20251001
A reply means the token works. A 401 means it was revoked; mint a new one through the OAuth pool in Settings.
This wiki is generated from wiki/src/content/docs in cuongtranba/kanna.
Edit the docs there: changes made on this wiki are overwritten on the next sync.
The same docs are published at https://kanna-wiki.lowbit.link.
Getting Started
Features
- Providers & Models
- Chat & Transcript
- Generative UI
- Projects & Sessions
- Boards
- Multi-repo Stacks
- Slash Commands
- Cron Jobs
- Loops
- Beacons
- Kanna Plugins
- Package Auto-Update
- Advanced
- Security
Guides
- User Guide
- Contributing
- Ops & Self-Host
Sharing
Reference