Building Custom AI Skills with InstructLab Taxonomy
Create domain-specific AI capabilities using InstructLab's taxonomy system—from writing skill definitions to generating synthetic training data and validating fine-tuned models.
Running OpenClaw via Docker Compose on Linux creates a classic volume permission challenge: the container’s node user (UID 1000) needs read/write access to directories that are owned by your host user (often azureuser on Azure VMs, which is also UID 1000 — but not always).
When permissions are wrong, you’ll see:
Docker containers on Linux share the host kernel, so file ownership is determined by numeric UIDs, not usernames:
Host: azureuser (UID 1000) ← same number
Container: node (UID 1000) ← same numberIf both are UID 1000, things generally “just work.” But there are common scenarios where they diverge:
| Scenario | Host UID | Container UID | Works? |
|---|---|---|---|
| Default Azure VM | 1000 | 1000 | ✅ |
| Root-created directories | 0 | 1000 | ❌ |
| Multi-user system | 1001+ | 1000 | ❌ |
| Custom container image | 1000 | varies | ❌ |
docker exec -it openclaw-openclaw-gateway-1 idExpected:
uid=1000(node) gid=1000(node) groups=1000(node)ls -lan ~/.openclaw/Look at the numeric UID/GID columns (3rd and 4th):
drwxrwxr-x 11 1000 1000 4096 Feb 25 23:36 .
-rw------- 1 1000 1000 2810 Feb 25 23:35 openclaw.json
drwx------ 3 1000 1000 4096 Feb 25 23:36 memoryIf the UID is 0 (root) or differs from the container’s UID, you need to fix it.
sudo chown -R 1000:1000 ~/.openclaw/memoryThis sets ownership to UID 1000 (the container’s node user) regardless of the host username.
If multiple directories have wrong ownership:
sudo chown -R 1000:1000 ~/.openclaw/Fix specific directories that need write access:
sudo chown -R 1000:1000 ~/.openclaw/memory
sudo chown -R 1000:1000 ~/.openclaw/logs
sudo chown -R 1000:1000 ~/.openclaw/agents
sudo chown -R 1000:1000 ~/.openclaw/credentialsAfter fixing ownership, set appropriate permission modes:
# Memory directories — owner + group read/write/execute
chmod 770 ~/.openclaw/memory
chmod 770 ~/.openclaw/memory/notes
# SQLite database — owner + group read/write
chmod 660 ~/.openclaw/memory/main.sqlite
# Config file — owner read/write only
chmod 600 ~/.openclaw/openclaw.json
# Credentials — restrictive
chmod 700 ~/.openclaw/credentials| Path | Mode | Rationale |
|---|---|---|
memory/ | 770 | Container needs read/write/list |
memory/notes/ | 770 | Agent writes Markdown notes |
memory/main.sqlite | 660 | Database read/write |
openclaw.json | 600 | Sensitive config (API keys) |
credentials/ | 700 | Most sensitive — tokens, keys |
logs/ | 700 | Gateway writes log files |
agents/ | 700 | Agent workspace state |
workspace/ | 770 | Shared workspace files |
The most reliable verification is to test from inside the container:
docker exec -it openclaw-openclaw-gateway-1 sh -lc '
echo test > /home/node/.openclaw/memory/._write_test && \
echo "WRITE OK" && \
rm -f /home/node/.openclaw/memory/._write_test
'If you see WRITE OK, the container can write to the memory directory.
Test all critical directories at once:
docker exec -it openclaw-openclaw-gateway-1 sh -lc '
for dir in memory memory/notes logs agents; do
path="/home/node/.openclaw/$dir"
if echo test > "$path/._test" 2>/dev/null; then
echo "✅ $dir: writable"
rm -f "$path/._test"
else
echo "❌ $dir: NOT writable"
fi
done
'OpenClaw’s docker-compose.yml maps the host’s ~/.openclaw into the container:
services:
openclaw-gateway:
image: openclaw:local
volumes:
- ~/.openclaw:/home/node/.openclaw
ports:
- "18789:18789"
- "18790:18790"Always create required directories before first run:
mkdir -p ~/.openclaw/memory/notes
mkdir -p ~/.openclaw/logs
mkdir -p ~/.openclaw/agents
mkdir -p ~/.openclaw/credentials
sudo chown -R 1000:1000 ~/.openclaw/If your host UID isn’t 1000, you can override the container’s user:
services:
openclaw-gateway:
image: openclaw:local
user: "${UID:-1000}:${GID:-1000}"
volumes:
- ~/.openclaw:/home/node/.openclawSet UID and GID in your .env file:
echo "UID=$(id -u)" >> ~/openclaw/.env
echo "GID=$(id -g)" >> ~/openclaw/.envCaveat: Changing the container user may break internal scripts that assume
nodeuser. Test thoroughly.
.openclaw Directory LayoutHere’s what a healthy installation looks like inside the container:
docker exec -it openclaw-openclaw-gateway-1 sh -lc \
'ls -la /home/node/.openclaw'drwxrwxr-x 11 node node 4096 .
drwx------ 3 node node 4096 agents
drwxr-xr-x 2 node node 4096 canvas
drwxr-xr-x 2 node node 4096 completions
drwx------ 2 node node 4096 credentials
drwxr-xr-x 2 node node 4096 cron
drwxrwxr-x 2 node node 4096 identity
drwx------ 2 node node 4096 logs
drwx------ 3 node node 4096 memory
-rw------- 1 node node 2810 openclaw.json
-rw------- 1 node node 2779 openclaw.json.bak
-rw------- 1 node node 2723 openclaw.json.bak.1
-rw------- 1 node node 2685 openclaw.json.bak.2
-rw------- 1 node node 2654 openclaw.json.bak.3
-rw------- 1 node node 2621 openclaw.json.bak.4
-rw-r--r-- 1 node node 49 update-check.json
drwxrwxr-x 4 node node 4096 workspaceNotice the backup chain: openclaw.json.bak through .bak.4 — OpenClaw keeps the last 5 config versions.
docker exec openclaw-openclaw-gateway-1 idls -lan ~/.openclaw/sudo chown -R <container-uid>:<container-gid> ~/.openclaw/chmod 770 ~/.openclaw/memorydocker compose restart openclaw-gatewayAI & Cloud Advisor with 18+ years experience. Author of 8 technical books, creator of Ansible Pilot. Speaker at KubeCon EU & Red Hat Summit 2026.
Create domain-specific AI capabilities using InstructLab's taxonomy system—from writing skill definitions to generating synthetic training data and validating fine-tuned models.
How to access the OpenClaw Control UI dashboard from an Azure VM — via SSH tunnel (secure) or public IP. Covers device pairing, dashboard authentication, and the browser-based management interface.
End-to-end guide to building a complete persistent memory system for your OpenClaw AI agent. Combine memory flush, hybrid search, file-backed notes, SQLite indexing, and session hooks into a cohesive knowledge architecture.