<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Danil Borkov</title>
    <description>The latest articles on DEV Community by Danil Borkov (@vokrob).</description>
    <link>https://dev.to/vokrob</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2333128%2F22cdeb6d-3a4e-45ee-abcd-0026c8212dc6.png</url>
      <title>DEV Community: Danil Borkov</title>
      <link>https://dev.to/vokrob</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vokrob"/>
    <language>en</language>
    <item>
      <title>How I set up OpenClaw on NixOS</title>
      <dc:creator>Danil Borkov</dc:creator>
      <pubDate>Thu, 23 Jul 2026 18:14:39 +0000</pubDate>
      <link>https://dev.to/vokrob/how-i-set-up-openclaw-on-nixos-1mfi</link>
      <guid>https://dev.to/vokrob/how-i-set-up-openclaw-on-nixos-1mfi</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Every new dialogue with an assistant is a blank slate. It doesn't remember what we talked about in previous sessions. I wasn't okay with that. I started looking for a solution and settled on &lt;a href="https://github.com/openclaw/nix-openclaw" rel="noopener noreferrer"&gt;OpenClaw&lt;/a&gt;: declarative configuration, long-term memory via &lt;a href="https://github.com/tobi/qmd" rel="noopener noreferrer"&gt;QMD&lt;/a&gt;, Telegram bot, GLM-4.7-flash — all reproducible on any machine with a single command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqkn3ewb7dkn84d11vlgy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqkn3ewb7dkn84d11vlgy.png" alt="Architecture" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is &lt;strong&gt;OpenClaw Gateway&lt;/strong&gt;. Attached to it are &lt;strong&gt;Telegram&lt;/strong&gt; (communication channel), &lt;strong&gt;Workspace&lt;/strong&gt; (files defining the assistant's personality), &lt;strong&gt;LLM&lt;/strong&gt; (the model generating responses), and &lt;strong&gt;QMD&lt;/strong&gt; (long-term memory via vector search). Everything is described in Nix.&lt;/p&gt;

&lt;p&gt;Before NixOS, I was on Arch. Updates regularly broke the system: package versions drifted apart, dependencies didn't match. Plus I had to handle rollbacks manually. With NixOS, you configure it and forget it. If something goes wrong, roll back to a commit or boot a previous generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking down flake.nix
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"NixOS configuration with Hyprland"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nixpkgs"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;home-manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github:nix-community/home-manager"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;home-manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;follows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nixpkgs"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;agenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github:ryantm/agenix"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;agenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;follows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nixpkgs"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;nix-openclaw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github:openclaw/nix-openclaw"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;openclaw-workspace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"path:/home/vokrob/.config/openclaw"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;flake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nv"&gt;outputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;home-manager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;agenix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;nix-openclaw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;openclaw-workspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;nixosConfigurations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;vokrob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;nixosSystem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;specialArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kn"&gt;inherit&lt;/span&gt; &lt;span class="nv"&gt;nix-openclaw&lt;/span&gt; &lt;span class="nv"&gt;openclaw-workspace&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="nv"&gt;system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"x86_64-linux"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;modules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nv"&gt;home-manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;nixosModules&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;home-manager&lt;/span&gt;
        &lt;span class="nv"&gt;agenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;nixosModules&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;
        &lt;span class="sx"&gt;./hosts/nixos&lt;/span&gt;
      &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;nix-openclaw&lt;/strong&gt;: flake from &lt;code&gt;github:openclaw/nix-openclaw&lt;/code&gt;. Pulls in Home Manager modules, a package overlay, and a binary cache on &lt;code&gt;cache.garnix.io&lt;/code&gt;. Garnix is useful because building OpenClaw doesn't rebuild dependencies from scratch;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;openclaw-workspace&lt;/strong&gt;: just a path, not a flake. A directory with files that define how the assistant behaves;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;specialArgs&lt;/strong&gt;: passes external inputs into the Nix module system. Without it, modules won't see &lt;code&gt;openclaw-workspace&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I only have one host, but if I added a server or a laptop, the configuration would stay the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modular architecture
&lt;/h2&gt;

&lt;p&gt;The configuration is split into two levels.&lt;/p&gt;

&lt;h3&gt;
  
  
  System modules
&lt;/h3&gt;

&lt;p&gt;Located in &lt;code&gt;modules/nixos/default.nix&lt;/code&gt;. This includes base.nix (bootloader, kernel), networking.nix (NetworkManager), services.nix (Hyprland and AmneziaWG for me), security.nix (disabled password for power-off via Polkit), and users.nix (vokrob, agenix, zsh).&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;base.nix&lt;/code&gt; I add the overlay:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;overlays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nv"&gt;nix-openclaw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;overlays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="sx"&gt;../../overlays&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The overlay adds OpenClaw packages to &lt;code&gt;pkgs&lt;/code&gt;, including &lt;code&gt;openclaw-gateway&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  User modules
&lt;/h3&gt;

&lt;p&gt;Imported via &lt;code&gt;modules/home/default.nix&lt;/code&gt;. The main one is &lt;code&gt;features/openclaw.nix&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Plus in &lt;code&gt;hosts/nixos/default.nix&lt;/code&gt; I added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;home-manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;sharedModules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;nix-openclaw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;homeManagerModules&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;openclaw&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sharedModules&lt;/code&gt; makes the OpenClaw module available in all Home Manager configurations. If a second host appears, the module is already there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking down openclaw.nix
&lt;/h2&gt;

&lt;p&gt;The main configuration is described in &lt;code&gt;modules/home/features/openclaw.nix&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workspace
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;programs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;openclaw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;workspace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;bootstrapFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;agents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;openclaw-workspace&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/AGENTS.md"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;soul&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;openclaw-workspace&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/SOUL.md"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;openclaw-workspace&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/TOOLS.md"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;identity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;openclaw-workspace&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/IDENTITY.md"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;openclaw-workspace&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/USER.md"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These five files shape the assistant's personality: &lt;code&gt;AGENTS.md&lt;/code&gt; defines agent roles and routing, &lt;code&gt;SOUL.md&lt;/code&gt; is the base instruction, &lt;code&gt;TOOLS.md&lt;/code&gt; describes tools, &lt;code&gt;IDENTITY.md&lt;/code&gt; sets communication style, &lt;code&gt;USER.md&lt;/code&gt; holds my data.&lt;/p&gt;

&lt;p&gt;The files are in &lt;code&gt;~/.config/openclaw/&lt;/code&gt;, and you can change them without rebuilding the system. Just edit &lt;code&gt;SOUL.md&lt;/code&gt;, and the assistant will start behaving differently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secrets
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;programs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;openclaw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;ZHIPU_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/run/agenix/openclaw-zhipu-key"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;OPENCLAW_GATEWAY_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/run/agenix/openclaw-gateway-token"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The values are not the keys themselves, but paths to files. OpenClaw reads secrets from files. Nix substitutes paths at build time, and agenix decrypts secrets during configuration activation and places them in &lt;code&gt;/run/agenix/&lt;/code&gt;. They never end up in &lt;code&gt;/nix/store/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The Telegram token is passed separately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;channels&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;telegram&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;tokenFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/run/agenix/openclaw-telegram-token"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Integrations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;gateway&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"local"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;channels&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;telegram&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;tokenFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/run/agenix/openclaw-telegram-token"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;allowFrom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5748618304&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;local&lt;/code&gt; mode means the gateway runs without being tied to the OpenClaw Cloud. Everything is on my machine. The gateway listens on a local port and authorizes requests via a token from &lt;code&gt;/run/agenix/openclaw-gateway-token&lt;/code&gt;. Only I have access to the Telegram bot (the &lt;code&gt;allowFrom&lt;/code&gt; list).&lt;/p&gt;

&lt;h3&gt;
  
  
  GLM-4.7-flash
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;providers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;openai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://open.bigmodel.cn/api/paas/v4"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ZHIPU_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nv"&gt;models&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"glm-4.7-flash"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"glm-4.7-flash"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"openai-completions"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;contextWindow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The provider is called &lt;code&gt;openai&lt;/code&gt;, but &lt;code&gt;baseUrl&lt;/code&gt; points to Z.ai. Z.ai provides an OpenAI-compatible endpoint. Through &lt;code&gt;api = "openai-completions"&lt;/code&gt;, OpenClaw uses the standard OpenAI SDK. The &lt;code&gt;glm-4.7-flash&lt;/code&gt; model is free, with a 200K token context window.&lt;/p&gt;

&lt;h3&gt;
  
  
  QMD — long-term memory
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"qmd"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;QMD is a Qdrant-based sidecar that doesn't require a separate server.&lt;/p&gt;

&lt;p&gt;Each message and response is vectorized, and embeddings go into QMD with metadata. When I write something new, semantic search picks up fragments from the past and mixes them into the prompt. The assistant remembers what we talked about in previous sessions.&lt;/p&gt;

&lt;p&gt;I tested it — it remembers:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4xjb1ufdsrm0baoyx1bs.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4xjb1ufdsrm0baoyx1bs.jpg" alt="Memory" width="800" height="1719"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The "remember" and "forget" commands work through &lt;code&gt;MEMORY.md&lt;/code&gt;. QMD just adds semantic search over the entire history on top.&lt;/p&gt;

&lt;h3&gt;
  
  
  Elevated privileges
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;elevated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;allowFrom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;telegram&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5748618304&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This section grants access to dangerous tools via the Telegram bot: executing commands on the host, installing packages, managing processes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional options
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;agents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"openai/glm-4.7-flash"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;thinkingDefault&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"low"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;compaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;reserveTokensFloor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nv"&gt;reloadScript&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;bundledPlugins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;summarize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;thinkingDefault&lt;/code&gt;: reasoning depth. Set to &lt;code&gt;low&lt;/code&gt; — everyday commands don't need deep thinking;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;compaction.reserveTokensFloor&lt;/code&gt;: reserves 20K tokens for memory and system prompt. When I didn't reserve it, the context got eaten up and the assistant started making silly mistakes;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reloadScript&lt;/code&gt;: generates a config reload script without restarting the gateway;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bundledPlugins.summarize&lt;/code&gt;: automatically summarizes URLs and PDFs sent in the chat.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The gateway runs as a systemd user service: it starts when I log in and restarts if it crashes. I check logs via &lt;code&gt;journalctl --user -u openclaw-gateway -f&lt;/code&gt;. If something goes wrong, I run &lt;code&gt;systemctl --user restart openclaw-gateway&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft1th3vyc50cgvcknp0xd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft1th3vyc50cgvcknp0xd.png" alt="Service" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Secrets
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;secrets/&lt;/code&gt; there are three encrypted files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;openclaw-telegram-token.age&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openclaw-zhipu-key.age&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openclaw-gateway-token.age&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the config it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;age&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;secrets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"openclaw-telegram-token"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;../../secrets/openclaw-telegram-token.age&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"vokrob"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0400"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agenix encrypts files using &lt;code&gt;age&lt;/code&gt;. The key is stored locally at &lt;code&gt;~/.config/agenix/age-key.txt&lt;/code&gt;. Decryption only happens during system activation. Secrets never end up in &lt;code&gt;/nix/store/&lt;/code&gt;, so they can't be accidentally committed or exposed in the binary cache. Even if someone gains access to the store, they won't see the API keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Message lifecycle
&lt;/h2&gt;

&lt;p&gt;I send a message to the Telegram bot. The Telegram bot sends it to the gateway. The gateway first checks &lt;code&gt;allowFrom&lt;/code&gt; — if I'm on the list, it proceeds. Then the gateway queries QMD for context from past conversations. It builds a prompt: system instructions + memory context + my message. It sends this to GLM-4.7-flash. The model's response is saved as a new memory fragment by the gateway and sent back to Telegram.&lt;/p&gt;

&lt;p&gt;The chain takes anywhere from a few seconds to several minutes due to the queue for the free model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problems
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Service won't start
&lt;/h3&gt;

&lt;p&gt;The first thing I ran into: the gateway wouldn't start. The reason: agenix didn't apply the activation script. Fixed by running &lt;code&gt;sudo nixos-rebuild switch&lt;/code&gt;, then checking &lt;code&gt;/run/agenix/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /run/agenix/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the files aren't there, there's an error in the &lt;code&gt;age.secrets&lt;/code&gt; definitions. In my case, I forgot to specify &lt;code&gt;owner&lt;/code&gt; and &lt;code&gt;mode&lt;/code&gt;, so agenix didn't create the files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Telegram bot doesn't respond
&lt;/h3&gt;

&lt;p&gt;Two possible causes: either &lt;code&gt;allowFrom&lt;/code&gt; is wrong, or the token is invalid. You can find out your Telegram ID via &lt;a href="https://t.me/userinfobot" rel="noopener noreferrer"&gt;@userinfobot&lt;/a&gt;. I made a mistake: I entered the chat ID instead of the user ID.&lt;/p&gt;

&lt;p&gt;To check the token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; openclaw-gateway | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; telegram
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  QMD doesn't return context
&lt;/h3&gt;

&lt;p&gt;Right after setup, QMD is empty and the assistant responds as if it has no memory. This is normal. Embeddings accumulate as you interact. You just need to keep talking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gateway token mismatch
&lt;/h3&gt;

&lt;p&gt;This can happen if you recreate the token without the gateway knowing about it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;agenix &lt;span class="nt"&gt;-e&lt;/span&gt; secrets/openclaw-gateway-token.age
systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; restart openclaw-gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It took an evening, but it was worth it. Now I have a reproducible AI assistant that remembers what we talked about and deploys with a single command. The configuration is identical on any machine: clone the repo, run &lt;code&gt;nixos-rebuild&lt;/code&gt;, and you're done.&lt;/p&gt;

&lt;p&gt;Code on &lt;a href="https://github.com/vokrob/nixos-config" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Replace the secrets with your own and build.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
