# Deploying nanobot on NixOS This documents what it took to get nanobot running on Camellia (NixOS 26.05). See `/etc/nixos/configuration.nix` for the full config — every block below has a matching section there. ## 1. Base system ```nix # Hostname, timezone, SSH, fail2ban networking.hostName = "Camellia"; time.timeZone = "Asia/Shanghai"; services.openssh = { enable = true; ... }; services.fail2ban = { enable = true; }; ``` ## 2. Required packages ```nix environment.systemPackages = with pkgs; [ git curl wget vim tmux htop jq fd ripgrep python313 uv # nanobot runtime nodejs_22 # for some skills/tools ]; ``` ## 3. Users Create a dedicated user for nanobot with lingering enabled: ```nix users.users.claw = { extraGroups = ["sudo"]; isNormalUser = true; linger = true; # user services survive logout }; security.sudo.extraRules = [ { users = ["claw"]; commands = [{ command = "ALL"; options = ["NOPASSWD"]; }]; } ]; ``` ## 4. FHS symlinks nanobot's `exec` tool (and many scripts) expect `/bin/bash`: ```nix systemd.tmpfiles.rules = [ "L+ /bin/bash - - - - /run/current-system/sw/bin/bash" "L+ /usr/bin/env - - - - /run/current-system/sw/bin/env" ]; ``` ## 5. nanobot systemd user service ```nix systemd.user.services.nanobot = { description = "nanobot AI assistant gateway"; wantedBy = ["default.target"]; serviceConfig = { Type = "simple"; ExecStart = "/home/claw/nanobot-src/.venv/bin/python -m nanobot gateway --foreground --port 18790"; Restart = "always"; RestartSec = 5; WorkingDirectory = "/home/claw/.nanobot/workspace"; Environment = "HOME=/home/claw"; }; }; ``` Use a git checkout (`/home/claw/nanobot-src`) so you can `git checkout known-good` to rollback. ## 6. Watchdog timer Covers the case where systemd misses a crash: ```nix systemd.user.services.nanobot-watchdog = { serviceConfig = { Type = "oneshot"; ExecStart = "${pkgs.systemd}/bin/systemctl --user start nanobot"; }; }; systemd.user.timers.nanobot-watchdog = { timerConfig = { OnUnitActiveSec = "60s"; }; }; ``` ## 7. Reverse proxy (Caddy) nanobot's gateway binds to `127.0.0.1` by default and rejects non-local connections. For a dedicated VM this is over-cautious, so we use Caddy to reverse-proxy the WebSocket UI externally: ```nix services.caddy = { enable = true; virtualHosts."http://192.168.122.194".extraConfig = '' reverse_proxy 127.0.0.1:8765 ''; }; ``` ## 8. Proxy for outbound traffic (v2rayA) If the LLM API and Telegram are blocked: ```nix services.v2raya = { enable = true; }; ``` Then configure proxy env vars in `~/.bashrc`: ```bash export http_proxy=http://127.0.0.1:20171 export https_proxy=http://127.0.0.1:20171 export all_proxy=socks5://127.0.0.1:20170 ``` ## 9. OOM protection nanobot can spike memory. Without this, the kernel OOM killer takes it down: ```nix services.earlyoom = { enable = true; freeMemThreshold = 5; freeSwapThreshold = 10; }; swapDevices = [{ device = "/swapfile"; size = 2048; }]; # 2 GB ``` ## 10. Firewall ```nix networking.firewall.allowedTCPPorts = [ 22 # SSH 80 # Caddy HTTP 2017 # v2rayA web UI 20170 # v2rayA SOCKS5 20171 # v2rayA HTTP proxy ]; ``` ## 11. Kernel hardening ```nix boot.kernel.sysctl = { "net.ipv4.tcp_syncookies" = 1; "net.ipv4.conf.all.rp_filter" = 1; "net.ipv4.conf.all.accept_redirects" = 0; "net.ipv6.conf.all.accept_redirects" = 0; "kernel.kptr_restrict" = 1; "kernel.dmesg_restrict" = 1; }; ``` ## 12. Nix GC ```nix nix.gc = { automatic = true; dates = "weekly"; options = "--delete-older-than 30d"; }; ``` ## 13. Mirrors (for China) ```nix nix.settings.substituters = ["https://mirrors.tuna.tsinghua.edu.cn/nix-channels/store"]; ``` ## 14. Install nanobot itself ```bash # As claw user git clone https://github.com/HKUDS/nanobot.git ~/nanobot-src cd ~/nanobot-src uv sync uv run nanobot onboard # interactive setup wizard # Populate workspace: SOUL.md, USER.md, memory/MEMORY.md ``` ## 15. Apply Use the safe-switch wrapper to rebuild with health checks: ```bash sudo /home/claw/.nanobot/workspace/scripts/safe-switch.sh ``` This runs `nixos-rebuild switch`, waits, then verifies the gateway is reachable. If the health check fails, it automatically rolls back. ## Known quirks - **sudo path**: Use `/run/wrappers/bin/sudo` inside scripts, not the default PATH's `/run/current-system/sw/bin/sudo` (lacks setuid). - **systemctl from cron/timers**: Need `XDG_RUNTIME_DIR=/run/user/1000 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus`. - **Gateway self-restart**: Never `systemctl --user restart nanobot` from within nanobot's own process — it kills the parent. Use `nohup bash -c 'sleep 2 && ...' &`. - **Rollback**: `cd ~/nanobot-src && git checkout known-good && systemctl --user restart nanobot`.