#Docker run grab .env variables and build openclaw.json
1 messages · Page 1 of 1 (latest)
First, quick sanity check: what does openclaw --version show? Docker behavior has moved around a bit.
For the rebuild-often case, I’d avoid regenerating openclaw.json if you can. The Docker docs say Compose bind-mounts OPENCLAW_CONFIG_DIR to /home/node/.openclaw, and that mounted dir is where openclaw.json, auth profiles, and .env live. So the cleanest setup is:
OPENCLAW_CONFIG_DIR=/path/on/host/openclaw-config
OPENCLAW_WORKSPACE_DIR=/path/on/host/openclaw-workspace
OPENCLAW_GATEWAY_TOKEN=your-token
DISCORD_BOT_TOKEN=your-discord-token
Then in /path/on/host/openclaw-config/openclaw.json, reference env vars instead of writing secrets directly:
{
gateway: {
mode: "local",
bind: "lan",
auth: {
mode: "token",
token: { source: "env", provider: "default", id: "OPENCLAW_GATEWAY_TOKEN" },
},
},
channels: {
discord: {
enabled: true,
token: { source: "env", provider: "default", id: "DISCORD_BOT_TOKEN" },
},
},
}
That way the file is stable, rebuilds are cheap, and secrets stay in .env / Compose env.
If you really want to create/update config from env on every boot, use the CLI rather than hand-building JSON where possible, e.g. after the gateway container exists:
docker compose run --rm openclaw-cli config set channels.discord.token \
--ref-provider default --ref-source env --ref-id DISCORD_BOT_TOKEN
Docs:
https://docs.openclaw.ai/install/docker
https://docs.openclaw.ai/gateway/configuration
https://docs.openclaw.ai/gateway/secrets
node@openclaw:/app$ openclaw --version
OpenClaw 2026.3.25
Thanks. 2026.3.25 should be new enough for the env-backed SecretRef approach.
For your case, I’d make the config persistent and let it point at env vars, instead of rebuilding the JSON each time. Example shape:
{
gateway: {
mode: "local",
bind: "lan",
auth: {
mode: "token",
token: { source: "env", provider: "default", id: "OPENCLAW_GATEWAY_TOKEN" },
},
},
channels: {
discord: {
enabled: true,
token: { source: "env", provider: "default", id: "DISCORD_BOT_TOKEN" },
},
},
}
Then make sure your container gets those env vars and that /home/node/.openclaw is backed by a host volume/bind mount. In the official Docker flow that’s controlled by OPENCLAW_CONFIG_DIR.
After editing, validate from inside the container:
openclaw doctor
openclaw status --all
If you want to set the Discord token ref using the CLI instead of editing JSON:
openclaw config set channels.discord.token --ref-provider default --ref-source env --ref-id DISCORD_BOT_TOKEN
Docs for the exact pieces:
https://docs.openclaw.ai/install/docker
https://docs.openclaw.ai/gateway/secrets
https://docs.openclaw.ai/channels/discord
I want these settings
{
"gateway": {
"auth": {
"mode": "token",
"token": "18494ce009481af3123a641eb885173c6c00384b94d208a39"
},
"controlUi": {
"allowedOrigins": [
"https://openclaw.max.local"
]
},
"mode": "local",
"port": 18789,
"bind": "lan",
"tailscale": {
"mode": "off",
"resetOnExit": false
"models": {
"mode": "merge",
"providers": {
"lmstudio": {
"baseUrl": "http://192.168.168.20:1234/v1",
"api": "openai-completions",
"auth": "api-key",
"apiKey": "LM_API_TOKEN",
"models": [
{
},
"auth": {
"profiles": {
"lmstudio:default": {
"provider": "lmstudio",
"mode": "api_key"
}
Two important things:
- You pasted a live-looking gateway token in a public indexed help thread. Treat it as leaked and rotate it.
- Your snippet is structurally broken:
modelsand top-levelauthshould not be nested insidegateway.tailscale.
A cleaned-up version would look like this. Put the real secret values in .env, not directly in the JSON:
OPENCLAW_GATEWAY_TOKEN=replace-with-new-rotated-token
LM_API_TOKEN=your-lmstudio-token
{
gateway: {
mode: "local",
port: 18789,
bind: "lan",
auth: {
mode: "token",
token: { source: "env", provider: "default", id: "OPENCLAW_GATEWAY_TOKEN" },
},
controlUi: {
allowedOrigins: ["https://openclaw.max.local"],
},
tailscale: {
mode: "off",
resetOnExit: false,
},
},
models: {
mode: "merge",
providers: {
lmstudio: {
baseUrl: "http://192.168.168.20:1234/v1",
api: "openai-completions",
auth: "api-key",
apiKey: { source: "env", provider: "default", id: "LM_API_TOKEN" },
models: [
{
id: "REPLACE_WITH_LMSTUDIO_MODEL_ID",
name: "LM Studio local model",
},
],
},
},
},
auth: {
profiles: {
"lmstudio:default": {
provider: "lmstudio",
mode: "api_key",
},
},
},
}
Replace REPLACE_WITH_LMSTUDIO_MODEL_ID with the exact model id LM Studio exposes, then test:
openclaw doctor
openclaw models status --probe
openclaw status --all
Docs:
https://docs.openclaw.ai/gateway/configuration
https://docs.openclaw.ai/gateway/secrets
https://docs.openclaw.ai/concepts/models
give me .env varaibles for all of those keys
Yep. I’d use these names:
# Gateway
OPENCLAW_GATEWAY_TOKEN=replace-with-new-rotated-token
OPENCLAW_GATEWAY_MODE=local
OPENCLAW_GATEWAY_PORT=18789
OPENCLAW_GATEWAY_BIND=lan
OPENCLAW_CONTROL_UI_ORIGIN=https://openclaw.max.local
OPENCLAW_TAILSCALE_MODE=off
OPENCLAW_TAILSCALE_RESET_ON_EXIT=false
# LM Studio
LMSTUDIO_BASE_URL=http://192.168.168.20:1234/v1
LMSTUDIO_API=\\openai-completions
LMSTUDIO_AUTH=api-key
LM_API_TOKEN=your-lmstudio-token
LMSTUDIO_MODEL_ID=replace-with-exact-model-id-from-lm-studio
LMSTUDIO_MODEL_NAME=LM Studio local model
Small correction: for OpenClaw itself, env refs are best for secrets like gateway.auth.token and models.providers.lmstudio.apiKey. For regular config values like port/bind/origin/model id, either keep them directly in openclaw.json, or generate openclaw.json from a template with something like envsubst.
So the stable openclaw.json I’d pair with that .env is:
{
gateway: {
mode: "local",
port: 18789,
bind: "lan",
auth: {
mode: "token",
token: { source: "env", provider: "default", id: "OPENCLAW_GATEWAY_TOKEN" },
},
controlUi: {
allowedOrigins: ["https://openclaw.max.local"],
},
tailscale: {
mode: "off",
resetOnExit: false,
},
},
models: {
mode: "merge",
providers: {
lmstudio: {
baseUrl: "http://192.168.168.20:1234/v1",
api: "openai-completions",
auth: "api-key",
apiKey: { source: "env", provider: "default", id: "LM_API_TOKEN" },
models: [
{
id: "REPLACE_WITH_EXACT_MODEL_ID",
name: "LM Studio local model",
},
],
},
},
},
auth: {
profiles: {
"lmstudio:default": {
provider: "lmstudio",
mode: "api_key",
},
},
},
}
Also: rotate the gateway token you pasted earlier before using this. Public thread + token = assume compromised.
can you pass me to a human?