1- # OpenClaw Gateway — Development Dockerfile
1+ # OpenClaw Gateway — Docker Image
22#
3- # This image provides the OpenClaw gateway runtime environment for development.
4- # It includes a lightweight gateway stub that responds to /health and serves
5- # the config UI on port 18789.
6- #
7- # In production this is replaced with the real `openclaw/pod` image.
3+ # Base: oven/bun (Debian-based)
4+ # Node.js 24 installed via fnm
5+ # Includes: sqlite, cron, nproc
86#
97# Usage:
108# docker build -f docker/Dockerfile.openclaw -t openclaw/pod:latest .
119#
1210# Or via docker-compose (automatic):
1311# OPENCLAW_DATA_DIR=~/.openclaw docker compose -f docker/docker-compose.openclaw.yml up -d
1412
15- FROM node:20-alpine
16-
17- # Install runtime dependencies
18- RUN apk add --no-cache \
19- curl \
20- tini
21-
22- # Create non-root user for the gateway
23- RUN addgroup -g 1001 openclaw && \
24- adduser -u 1001 -G openclaw -s /bin/sh -D openclaw
25-
26- # Set working directory
27- WORKDIR /home/openclaw
28-
29- # Create gateway stub
30- RUN mkdir -p /home/openclaw/.openclaw && \
31- cat > /home/openclaw/gateway.js << 'GATEWAY_EOF'
32- const http = require('http');
33- const fs = require('fs');
34- const path = require('path');
35-
36- const PORT = parseInt(process.env.GATEWAY_PORT, 10) || 18789;
37- const DATA_DIR = '/home/openclaw/.openclaw';
38- const CONFIG_FILE = path.join(DATA_DIR, 'openclaw.json');
39-
40- // Ensure config exists
41- function ensureConfig() {
42- try {
43- if (!fs.existsSync(CONFIG_FILE)) {
44- const cfg = {
45- gateway: { host: '127.0.0.1', port: PORT },
46- agents: { defaults: { model: { primary: 'anthropic/claude-sonnet-4-20250514' } } },
47- models: { providers: {} }
48- };
49- fs.writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2));
50- }
51- } catch (e) {
52- console.error('Failed to write config:', e.message);
53- }
54- }
55-
56- function readConfig() {
57- try {
58- return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
59- } catch {
60- return { gateway: { host: '127.0.0.1', port: PORT } };
61- }
62- }
63-
64- const server = http.createServer((req, res) => {
65- res.setHeader('Content-Type', 'application/json');
66- res.setHeader('Access-Control-Allow-Origin', '*');
67-
68- if (req.method === 'GET' && req.url === '/health') {
69- res.writeHead(200);
70- res.end(JSON.stringify({
71- status: 'ok',
72- port: PORT,
73- uptime: process.uptime(),
74- version: '0.2.2-dev',
75- timestamp: new Date().toISOString()
76- }));
77- return;
78- }
13+ FROM oven/bun:latest
7914
80- if (req.method === 'GET' && req.url === '/status') {
81- const config = readConfig();
82- res.writeHead(200);
83- res.end(JSON.stringify({
84- state: 'running',
85- port: PORT,
86- config: config
87- }));
88- return;
89- }
15+ # Install system dependencies: fnm prerequisites, sqlite, cron, nproc (via coreutils)
16+ RUN apt-get update && \
17+ apt-get install -y --no-install-recommends \
18+ curl \
19+ unzip \
20+ sqlite3 \
21+ cron \
22+ coreutils \
23+ tini \
24+ && rm -rf /var/lib/apt/lists/*
9025
91- if (req.method === 'GET' && req.url === '/api/config') {
92- const config = readConfig();
93- res.writeHead(200);
94- res.end(JSON.stringify(config));
95- return;
96- }
26+ # Install fnm (Fast Node Manager)
27+ ENV FNM_DIR=/root/.fnm
28+ RUN curl -fsSL https://fnm.vercel.app/install | bash
9729
98- if (req.method === 'POST' && req.url === '/api/config') {
99- let body = '';
100- req.on('data', chunk => body += chunk);
101- req.on('end', () => {
102- try {
103- const cfg = JSON.parse(body);
104- fs.writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2));
105- res.writeHead(200);
106- res.end(JSON.stringify({ ok: true }));
107- } catch (e) {
108- res.writeHead(400);
109- res.end(JSON.stringify({ error: e.message }));
110- }
111- });
112- return;
113- }
30+ # Install and activate Node.js 24 via fnm
31+ ENV PATH="/root/.fnm:/root/.fnm/aliases/default/bin:$PATH"
32+ RUN /bin/bash -c 'source /root/.fnm/fnm.sh && fnm install 24 && fnm default 24'
11433
115- // Default: simple status page
116- res.writeHead(200);
117- res.end(JSON.stringify({
118- service: 'openclaw-gateway',
119- status: 'running',
120- port: PORT,
121- endpoints: ['/health', '/status', '/api/config']
122- }));
123- });
34+ # Ensure fnm is activated for all subsequent RUN commands and the final CMD
35+ ENV FNM_NODE_VERSION=24
36+ RUN /bin/bash -c 'source /root/.fnm/fnm.sh && fnm use 24 && node -v && npm -v'
12437
125- ensureConfig();
126- server.listen(PORT, '0.0.0.0', () => {
127- console.log(`OpenClaw Gateway (dev) listening on port ${PORT}`);
128- console.log(` Health: http://localhost:${PORT}/health`);
129- console.log(` Config: http://localhost:${PORT}/api/config`);
130- });
131- GATEWAY_EOF
38+ # Create data directory
39+ RUN mkdir -p /root/.openclaw
13240
133- # Set ownership
134- RUN chown -R openclaw:openclaw /home/ openclaw
41+ # Install OpenClaw globally via npm
42+ RUN /bin/bash -c 'source /root/.fnm/fnm.sh && fnm use 24 && npm install -g openclaw'
13543
13644# Expose gateway port
13745EXPOSE 18789
@@ -143,6 +51,5 @@ HEALTHCHECK --interval=10s --timeout=5s --start-period=30s --retries=5 \
14351# Use tini as init system for proper signal handling
14452ENTRYPOINT ["tini", "--"]
14553
146- # Default command: run the gateway in foreground
147- USER openclaw
148- CMD ["node", "gateway.js"]
54+ # Activate fnm + Node 24 and start the OpenClaw gateway
55+ CMD ["/bin/bash", "-c", "source /root/.fnm/fnm.sh && fnm use 24 && openclaw gateway run"]
0 commit comments