Self-host OpenClaw with Docker, Traefik, Authentik, and Telegram
A clean fresh-install guide for running OpenClaw on a VPS or homelab server behind Traefik and Authentik, with Telegram integration and no direct exposed app ports.
OpenClaw is most useful when it can live close to your infrastructure: it can read project files, help maintain services, answer Telegram messages, and act like an operator for your homelab.
This guide shows a fresh self-hosted OpenClaw deployment using Docker Compose, Traefik, Authentik, and a Telegram bot.
The design goal is simple:
1
2
3
4
5
6
7
Internet
↓
Traefik :443
↓
Authentik SSO
↓
OpenClaw Gateway inside Docker
OpenClaw itself does not publish a host port. It only listens inside Docker, and Traefik exposes it through HTTPS.
What we are building
We will deploy:
- OpenClaw Gateway container
- A persistent OpenClaw config/state directory
- A workspace directory for files and projects
- Traefik HTTPS routing
- Authentik forward-auth protection
- Telegram bot integration
- Optional host-folder access for homelab administration
The public URL in this guide will be:
1
https://agent.example.com
Replace it with your own domain.
Requirements
You need:
- A Linux server or VPS
- Docker and Docker Compose
- A working Traefik stack
- A Docker network shared with Traefik, for example
proxy - DNS pointing
agent.example.comto your server - Optional but recommended: Authentik already deployed behind Traefik
- A Telegram bot token from BotFather
This guide assumes Traefik already owns ports 80 and 443.
Folder layout
Keep the service self-contained:
1
2
sudo mkdir -p /home/ubuntu/openclaw/.openclaw/workspace
cd /home/ubuntu/openclaw
Recommended structure:
1
2
3
4
5
6
/home/ubuntu/openclaw/
├── .env
├── docker-compose.yml
└── .openclaw/
├── openclaw.json
└── workspace/
Create the environment file
Generate a gateway token. This is the token you will use to access the Web UI.
1
openssl rand -hex 32
Create .env:
1
nano /home/ubuntu/openclaw/.env
Example:
OPENCLAW_CONFIG_DIR=/home/ubuntu/openclaw/.openclaw
OPENCLAW_WORKSPACE_DIR=/home/ubuntu/openclaw/.openclaw/workspace
OPENCLAW_GATEWAY_TOKEN=replace_with_a_long_random_token
OPENCLAW_DISABLE_BONJOUR=1
OPENCLAW_TZ=UTC
Lock it down:
1
chmod 600 /home/ubuntu/openclaw/.env
Never commit this file to Git.
Create the OpenClaw config
Create the config file:
1
nano /home/ubuntu/openclaw/.openclaw/openclaw.json
Minimal example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
{
"gateway": {
"mode": "local",
"auth": {
"mode": "token",
"token": "replace_with_the_same_token_as_env"
},
"remote": {
"url": "ws://127.0.0.1:18789",
"token": "replace_with_the_same_token_as_env"
},
"port": 18789,
"bind": "lan",
"tailscale": {
"mode": "off",
"resetOnExit": false
},
"controlUi": {
"allowInsecureAuth": true,
"allowedOrigins": [
"http://localhost:18789",
"http://127.0.0.1:18789",
"https://agent.example.com"
]
}
},
"channels": {
"telegram": {
"enabled": true,
"botToken": "replace_with_telegram_bot_token",
"allowFrom": [
"replace_with_your_telegram_user_id"
],
"groups": {
"*": {
"requireMention": true
}
}
}
},
"plugins": {
"entries": {
"bonjour": {
"enabled": false
},
"openai": {
"enabled": true
}
}
}
}
Important notes:
gateway.auth.token,gateway.remote.token, andOPENCLAW_GATEWAY_TOKENshould match.- Add your real domain to
gateway.controlUi.allowedOrigins. allowFromshould contain only trusted Telegram user IDs.- Do not expose your Telegram bot token publicly.
Fix ownership for the container user. The official image normally runs as UID 1000:
1
2
3
sudo chown -R 1000:1000 /home/ubuntu/openclaw/.openclaw
sudo chmod 700 /home/ubuntu/openclaw/.openclaw
sudo chmod 600 /home/ubuntu/openclaw/.openclaw/openclaw.json
Docker Compose
Create docker-compose.yml:
1
nano /home/ubuntu/openclaw/docker-compose.yml
Paste this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
services:
openclaw-gateway:
image: ghcr.io/openclaw/openclaw:latest
container_name: openclaw-gateway
restart: unless-stopped
env_file:
- .env
environment:
HOME: /home/node
TERM: xterm-256color
OPENCLAW_CONFIG_DIR: /home/node/.openclaw
OPENCLAW_WORKSPACE_DIR: /home/node/.openclaw/workspace
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}
OPENCLAW_DISABLE_BONJOUR: ${OPENCLAW_DISABLE_BONJOUR:-1}
TZ: ${OPENCLAW_TZ:-UTC}
volumes:
- ./.openclaw:/home/node/.openclaw
- ./.openclaw/workspace:/home/node/.openclaw/workspace
cap_drop:
- NET_RAW
- NET_ADMIN
security_opt:
- no-new-privileges:true
extra_hosts:
- "host.docker.internal:host-gateway"
networks:
- proxy
init: true
command:
[
"node",
"dist/index.js",
"gateway",
"--bind",
"lan",
"--port",
"18789",
"--allow-unconfigured"
]
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
# HTTP → HTTPS
- "traefik.http.middlewares.openclaw-https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.openclaw.entrypoints=http"
- "traefik.http.routers.openclaw.rule=Host(`agent.example.com`)"
- "traefik.http.routers.openclaw.middlewares=openclaw-https-redirect"
# HTTPS route protected by Authentik
- "traefik.http.routers.openclaw-secure.entrypoints=https"
- "traefik.http.routers.openclaw-secure.rule=Host(`agent.example.com`)"
- "traefik.http.routers.openclaw-secure.middlewares=authentik@docker"
- "traefik.http.routers.openclaw-secure.tls=true"
- "traefik.http.routers.openclaw-secure.tls.certresolver=cloudflare"
- "traefik.http.routers.openclaw-secure.service=openclaw"
- "traefik.http.services.openclaw.loadbalancer.server.port=18789"
# Authentik outpost callback path must bypass the OpenClaw service
- "traefik.http.routers.authentik-openclaw-outpost.entrypoints=https"
- "traefik.http.routers.authentik-openclaw-outpost.rule=Host(`agent.example.com`) && PathPrefix(`/outpost.goauthentik.io/`)"
- "traefik.http.routers.authentik-openclaw-outpost.priority=100"
- "traefik.http.routers.authentik-openclaw-outpost.tls=true"
- "traefik.http.routers.authentik-openclaw-outpost.tls.certresolver=cloudflare"
- "traefik.http.routers.authentik-openclaw-outpost.service=authentik"
healthcheck:
test:
[
"CMD",
"node",
"-e",
"fetch('http://127.0.0.1:18789/healthz').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
]
interval: 30s
timeout: 5s
retries: 5
start_period: 20s
networks:
proxy:
external: true
Replace every agent.example.com with your real hostname.
Optional: give OpenClaw access to server folders
If you want OpenClaw to help manage homelab files, you can mount a host folder.
For example:
1
2
3
4
volumes:
- ./.openclaw:/home/node/.openclaw
- ./.openclaw/workspace:/home/node/.openclaw/workspace
- /home/ubuntu:/host/home/ubuntu
Inside OpenClaw, the host folder appears at:
1
/host/home/ubuntu
Be careful: this gives the agent broad file access. For safer setups, mount only specific service folders:
1
2
3
4
5
volumes:
- ./.openclaw:/home/node/.openclaw
- ./.openclaw/workspace:/home/node/.openclaw/workspace
- /home/ubuntu/traefik:/host/home/ubuntu/traefik
- /home/ubuntu/headscale:/host/home/ubuntu/headscale
Start OpenClaw
Validate Compose:
1
docker compose config
Start the gateway:
1
docker compose up -d
Check status:
1
docker compose ps
Expected result:
1
openclaw-gateway Up ... (healthy)
Check logs:
1
docker logs -f openclaw-gateway
You should see something like:
1
2
3
[gateway] http server listening
[gateway] ready
[telegram] starting provider
Authentik setup
Create an Authentik proxy application/provider for:
1
https://agent.example.com
Recommended values:
1
2
3
4
5
Application name: OpenClaw Agent
Slug: openclaw-agent
Provider type: Proxy Provider
Mode: Forward auth, single application
External host: https://agent.example.com
Then attach that provider to your embedded Authentik outpost.
Your Traefik labels already use:
1
- "traefik.http.routers.openclaw-secure.middlewares=authentik@docker"
So the request flow becomes:
1
Browser → Traefik → Authentik → OpenClaw
Access the Web UI
Open:
1
https://agent.example.com
You should pass through Authentik first.
Then enter your OpenClaw gateway token from .env:
1
OPENCLAW_GATEWAY_TOKEN
If you see this error:
1
origin not allowed
edit this file:
1
/home/ubuntu/openclaw/.openclaw/openclaw.json
and add your public URL to:
1
2
3
4
5
6
7
"gateway": {
"controlUi": {
"allowedOrigins": [
"https://agent.example.com"
]
}
}
Then restart:
1
docker compose restart openclaw-gateway
Approve browser/device pairing
The first Web UI connection may ask for device pairing:
1
device pairing required (requestId: ...)
List pending devices:
1
docker exec openclaw-gateway node dist/index.js devices pending
Approve the request:
1
docker exec openclaw-gateway node dist/index.js devices approve <requestId>
Then refresh the Web UI.
Telegram setup
Create a bot with BotFather:
1
2
/start
/newbot
Put the bot token in openclaw.json:
1
2
3
4
5
6
7
"channels": {
"telegram": {
"enabled": true,
"botToken": "replace_with_telegram_bot_token",
"allowFrom": ["replace_with_your_telegram_user_id"]
}
}
Restart:
1
docker compose restart openclaw-gateway
Send a message to your bot. If your user ID is in allowFrom, OpenClaw should reply.
Verify no direct port exposure
Run:
1
docker ps --filter name=openclaw-gateway --format 'table {{.Names}}\t{{.Ports}}\t{{.Status}}'
A secure Traefik-only setup should show no published port mapping for OpenClaw:
1
2
NAMES PORTS STATUS
openclaw-gateway Up ... (healthy)
That means OpenClaw is not exposed as 18789:18789 on the host.
The only public path is:
1
Internet → Traefik :443 → Authentik → OpenClaw container port 18789
Troubleshooting
Container says config is not readable
Fix ownership and permissions:
1
2
3
4
sudo chown -R 1000:1000 /home/ubuntu/openclaw/.openclaw
sudo chmod 700 /home/ubuntu/openclaw/.openclaw
sudo chmod 600 /home/ubuntu/openclaw/.openclaw/openclaw.json
docker compose restart openclaw-gateway
Gateway token mismatch
Make sure these three values match:
.env→OPENCLAW_GATEWAY_TOKENopenclaw.json→gateway.auth.tokenopenclaw.json→gateway.remote.token
Then restart:
1
docker compose restart openclaw-gateway
Authentik redirect works but WebSocket fails
Check that your domain is included in:
1
2
3
"allowedOrigins": [
"https://agent.example.com"
]
Also check Traefik is forwarding to the correct internal port:
1
- "traefik.http.services.openclaw.loadbalancer.server.port=18789"
Telegram does not reply
Check logs:
1
docker logs openclaw-gateway | grep -i telegram
Verify:
- The bot token is valid
channels.telegram.enabledistrue- Your Telegram user ID is in
allowFrom - You messaged the correct bot
Security checklist
Before using this seriously:
- Do not publish the gateway token.
- Do not publish the Telegram bot token.
- Keep OpenClaw behind Authentik or another trusted auth layer.
- Do not expose port
18789directly to the internet. - Mount only the host folders OpenClaw actually needs.
- Keep
.envpermissions at600. - Review paired devices periodically:
1
docker exec openclaw-gateway node dist/index.js devices list
Final architecture
A clean production-style deployment looks like this:
1
2
3
4
5
6
7
8
9
Cloudflare DNS
↓
Traefik :443
↓
Authentik forward-auth
↓
OpenClaw Gateway container
↓
Workspace + selected homelab folders
This gives you a practical self-hosted AI operator without opening extra ports or leaking secrets into your public documentation.
Related documentation
These posts connect to this topic and help build the bigger homelab picture:
- Build a homelab auth gateway with Traefik and Authentik — is the foundation for the protected HTTPS gateway used by OpenClaw.
- Build a Jekyll documentation site for your homelab — documents how to publish guides for the services OpenClaw helps operate.
- Self-host Karakeep for bookmarks and AI-assisted archives — adds a knowledge/archive layer that pairs naturally with an AI operator.
- Private Pi-hole DNS over Headscale with DNSCrypt and Authentik — shows the same homelab security model applied to private DNS.