Convert Openclaw to Podman container

- Use official ghcr.io/openclaw/openclaw image
- configure via JSON config file
- containerized for better isolation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
ashisgreat22 2026-03-18 01:35:02 +01:00
parent 11a588a4d9
commit a595445bd2
6 changed files with 173 additions and 215 deletions

64
braveapi.py Normal file
View file

@ -0,0 +1,64 @@
"""
Search Engine: Brave Search API
"""
import json
import os
from urllib.parse import quote
# About the engine
about = {
"website": "https://brave.com/search/api/",
"use_official_api": True,
"require_api_key": True,
"results": "JSON",
}
categories = ['general', 'web']
paging = True
max_page = 10
page_size = 20
# API Endpoint
base_url = "https://api.search.brave.com/res/v1/web/search"
def request(query, params):
# Get key from environment
api_key = os.getenv('BRAVE_API_KEY')
# Brave expects offset 0-9 for pages
pageno = min(params.get('pageno', 1), 10)
offset = pageno - 1
# Simple query string construction with proper quoting
# Using 'x-subscription-token' (lowercase) which was verified to work
params['url'] = f"{base_url}?q={quote(query)}&count=20&offset={offset}"
params['headers'] = {
'x-subscription-token': api_key,
'Accept': 'application/json'
}
params['method'] = 'GET'
# Clean up SearXNG defaults to prevent interference
if 'data' in params: del params['data']
if 'params' in params: del params['params']
return params
def response(resp):
results = []
data = json.loads(resp.text)
# The Brave API returns 'web' results
web_results = data.get('web', {}).get('results', [])
for item in web_results:
results.append({
'url': item.get('url'),
'title': item.get('title'),
'content': item.get('description'),
})
return results

View file

@ -88,10 +88,10 @@
};
# === OpenClaw ===
myModules.openclaw = {
myModules.openclaw-podman = {
enable = true;
port = 18789;
environmentFile = config.sops.templates."openclaw.env".path;
domain = "openclaw.ashisgreat.xyz";
};
# OpenClaw secrets
@ -99,10 +99,57 @@
sops.secrets.openclaw_zai_api_key = { };
sops.templates."openclaw.env" = {
owner = "openclaw";
content = ''
DISCORD_TOKEN=${config.sops.placeholder.openclaw_discord_token}
ZAI_API_KEY=${config.sops.placeholder.openclaw_zai_api_key}
'';
};
sops.templates."openclaw_config.json" = {
content = builtins.toJSON {
gateway = {
port = 18789;
bind = "0.0.0.0";
trustedProxies = ["::1", "127.0.0.1", "10.88.0.0/16", "10.89.0.0/16"];
auth = {
mode = "none";
};
controlUi = {
dangerouslyAllowHostHeaderOriginFallback = true;
allowedOrigins = ["*"];
};
};
channels = {
discord = {
enabled = true;
groupPolicy = "open";
dmPolicy = "open";
allowFrom = ["*"];
};
};
agents = {
defaults = {
model = {
primary = "zai/glm-5";
};
};
};
models = {
providers = {
zai = {
baseUrl = "https://api.z.ai/api/coding/paas/v4";
apiKey = "\${ZAI_API_KEY}";
api = "openai-completions";
models = [
{ id = "glm-4.7"; name = "GLM 4.7"; contextWindow = 128000; maxTokens = 131072; }
{ id = "glm-5"; name = "GLM 5"; contextWindow = 128000; maxTokens = 131072; }
{ id = "glm-5-turbo"; name = "GLM 5 Turbo"; contextWindow = 128000; maxTokens = 131072; }
{ id = "glm-4.5-air"; name = "GLM 4.5 Air"; contextWindow = 128000; maxTokens = 131072; }
{ id = "glm-4.7-flash"; name = "GLM 4.7 Flash"; contextWindow = 128000; maxTokens = 131072; }
];
};
};
};
};
};
}

View file

@ -5,6 +5,6 @@
./podman.nix
./nginx.nix
./searxng.nix
./openclaw.nix
./openclaw-podman.nix
];
}

View file

@ -1,96 +0,0 @@
{
"gateway": {
"port": 18789,
"bind": "loopback",
"trustedProxies": ["::1", "127.0.0.1", "10.88.0.0/16", "10.89.0.0/16"],
"auth": {
"mode": "none"
},
"controlUi": {
"dangerouslyAllowHostHeaderOriginFallback": true,
"allowedOrigins": ["*"]
}
},
"channels": {
"whatsapp": {
"enabled": false
},
"discord": {
"enabled": true,
"groupPolicy": "open",
"dmPolicy": "open",
"allowFrom": ["*"]
}
},
"agents": {
"defaults": {
"model": {
"primary": "zai/glm-5"
},
"memorySearch": {
"provider": "openai",
"model": "nomic-embed-text",
"remote": {
"baseUrl": "http://localhost:11434/v1"
}
}
}
},
"tools": {
"exec": {
"security": "full",
"ask": "off"
}
},
"skills": {
"entries": {}
},
"commands": {
"native": true,
"nativeSkills": "auto",
"restart": true,
"ownerDisplay": "raw"
},
"models": {
"mode": "merge",
"providers": {
"zai": {
"baseUrl": "https://api.z.ai/api/coding/paas/v4",
"apiKey": "${ZAI_API_KEY}",
"api": "openai-completions",
"models": [
{
"id": "glm-4.7",
"name": "GLM 4.7",
"contextWindow": 128000,
"maxTokens": 131072
},
{
"id": "glm-5",
"name": "GLM 5",
"contextWindow": 128000,
"maxTokens": 131072
},
{
"id": "glm-5-turbo",
"name": "GLM 5 Turbo",
"contextWindow": 128000,
"maxTokens": 131072
},
{
"id": "glm-4.5-air",
"name": "GLM 4.5 Air",
"contextWindow": 128000,
"maxTokens": 131072
},
{
"id": "glm-4.7-flash",
"name": "GLM 4.7 Flash",
"contextWindow": 128000,
"maxTokens": 131072
}
]
}
}
}
}

View file

@ -0,0 +1,58 @@
# OpenClaw Podman Module
# Provides: AI Agent with Discord integration running in a container
#
# Usage:
# myModules.openclaw-podman = {
# enable = true;
# port = 18789;
# domain = "openclaw.example.com";
# };
{
config,
lib,
pkgs,
...
}:
let
cfg = config.myModules.openclaw-podman;
in
{
options.myModules.openclaw-podman = {
enable = lib.mkEnableOption "OpenClaw AI Agent (Podman)";
port = lib.mkOption {
type = lib.types.port;
default = 18789;
description = "Gateway port for OpenClaw";
};
domain = lib.mkOption {
type = lib.types.str;
example = "openclaw.example.com";
description = "Public domain for OpenClaw";
};
};
config = lib.mkIf cfg.enable {
# Enable podman
myModules.podman.enable = true;
# OpenClaw container
virtualisation.oci-containers.containers."openclaw" = {
image = "ghcr.io/openclaw/openclaw:latest";
ports = [ "127.0.0.1:${toString cfg.port}:8080" ];
environmentFiles = [
config.sops.templates."openclaw.env".path
];
volumes = [
"${config.sops.templates."openclaw_config.json".path}:/root/.openclaw/config.json:ro"
"openclaw-data:/root/.openclaw"
];
extraOptions = [
"--network=host"
];
};
};
}

View file

@ -1,115 +0,0 @@
# OpenClaw Module
# Provides: AI Agent with Discord integration
#
# Usage:
# myModules.openclaw = {
# enable = true;
# port = 18789;
# };
{
config,
lib,
pkgs,
...
}:
let
cfg = config.myModules.openclaw;
configDir = "/var/lib/openclaw/config";
dataDir = "/var/lib/openclaw/data";
workspaceDir = "/var/lib/openclaw/workspace";
in
{
options.myModules.openclaw = {
enable = lib.mkEnableOption "OpenClaw AI Agent";
port = lib.mkOption {
type = lib.types.port;
default = 18789;
description = "Gateway port for OpenClaw";
};
environmentFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "Path to environment file with API keys";
};
};
config = lib.mkIf cfg.enable {
# Create user for openclaw
users.users.openclaw = {
isSystemUser = true;
group = "openclaw";
home = "/var/lib/openclaw";
createHome = true;
};
users.groups.openclaw = { };
# Create directories
systemd.tmpfiles.settings."10-openclaw" = {
"${configDir}".d = {
user = "openclaw";
group = "openclaw";
mode = "0700";
};
"${dataDir}".d = {
user = "openclaw";
group = "openclaw";
mode = "0700";
};
"${workspaceDir}".d = {
user = "openclaw";
group = "openclaw";
mode = "0700";
};
};
# Copy config file
environment.etc."openclaw/openclaw.json".source = ./openclaw-config.json;
# Systemd service
systemd.services.openclaw = {
description = "OpenClaw AI Agent";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = lib.mkMerge [
{
Type = "simple";
User = "openclaw";
Group = "openclaw";
WorkingDirectory = dataDir;
Environment = [
"NODE_ENV=production"
"OPENCLAW_CONFIG_DIR=${configDir}"
"OPENCLAW_DATA_DIR=${dataDir}"
"OPENCLAW_WORKSPACE_DIR=${workspaceDir}"
"PATH=${pkgs.nodejs_22}/bin:${pkgs.git}/bin:${pkgs.bash}/bin:${pkgs.coreutils}/bin"
];
ExecStartPre = [
"${pkgs.coreutils}/bin/mkdir -p ${configDir} ${dataDir} ${workspaceDir} /var/lib/openclaw/.openclaw"
"${pkgs.bash}/bin/bash -c 'cp -n /etc/openclaw/openclaw.json /var/lib/openclaw/.openclaw/openclaw.json || true'"
];
ExecStart = "${pkgs.nodejs_22}/bin/npx openclaw gateway --port ${toString cfg.port} --allow-unconfigured";
Restart = "on-failure";
RestartSec = "10s";
# Security
PrivateTmp = true;
ProtectSystem = "strict";
ReadWritePaths = [ "/var/lib/openclaw" "/var/lib/openclaw/.openclaw" configDir dataDir workspaceDir ];
NoNewPrivileges = true;
}
(lib.mkIf (cfg.environmentFile != null) {
EnvironmentFile = cfg.environmentFile;
})
];
};
};
}