{"id":616,"date":"2026-03-21T22:52:57","date_gmt":"2026-03-21T14:52:57","guid":{"rendered":"https:\/\/pa.yingzhi8.cn\/index.php\/2026\/03\/21\/install-docker-vm-runtime\/"},"modified":"2026-03-21T23:23:48","modified_gmt":"2026-03-21T15:23:48","slug":"install-docker-vm-runtime","status":"publish","type":"post","link":"https:\/\/pa.yingzhi8.cn\/index.php\/2026\/03\/21\/install-docker-vm-runtime\/","title":{"rendered":"Docker VM Runtime"},"content":{"rendered":"<h1>Docker VM Runtime<\/h1>\n<p>Shared runtime steps for VM-based Docker installs such as GCP, Hetzner, and similar VPS providers.<\/p>\n<h2>Bake required binaries into the image<\/h2>\n<p>Installing binaries inside a running container is a trap.<br \/>\nAnything installed at runtime will be lost on restart.<\/p>\n<p>All external binaries required by skills must be installed at image build time.<\/p>\n<p>The examples below show three common binaries only:<\/p>\n<ul>\n<li><code>gog<\/code> for Gmail access<\/li>\n<li><code>goplaces<\/code> for Google Places<\/li>\n<li><code>wacli<\/code> for WhatsApp<\/li>\n<\/ul>\n<p>These are examples, not a complete list.<br \/>\nYou may install as many binaries as needed using the same pattern.<\/p>\n<p>If you add new skills later that depend on additional binaries, you must:<\/p>\n<ol>\n<li>Update the Dockerfile<\/li>\n<li>Rebuild the image<\/li>\n<li>Restart the containers<\/li>\n<\/ol>\n<p><strong>Example Dockerfile<\/strong><\/p>\n<p>&#8220;`dockerfile  theme={&#8220;theme&#8221;:{&#8220;light&#8221;:&#8221;min-light&#8221;,&#8221;dark&#8221;:&#8221;min-dark&#8221;}}<br \/>\nFROM node:24-bookworm<\/p>\n<p>RUN apt-get update &amp;&amp; apt-get install -y socat &amp;&amp; rm -rf \/var\/lib\/apt\/lists\/*<\/p>\n<h1>Example binary 1: Gmail CLI<\/h1>\n<p>RUN curl -L https:\/\/github.com\/steipete\/gog\/releases\/latest\/download\/gog_Linux_x86_64.tar.gz<br \/>\n  | tar -xz -C \/usr\/local\/bin &amp;&amp; chmod +x \/usr\/local\/bin\/gog<\/p>\n<h1>Example binary 2: Google Places CLI<\/h1>\n<p>RUN curl -L https:\/\/github.com\/steipete\/goplaces\/releases\/latest\/download\/goplaces_Linux_x86_64.tar.gz<br \/>\n  | tar -xz -C \/usr\/local\/bin &amp;&amp; chmod +x \/usr\/local\/bin\/goplaces<\/p>\n<h1>Example binary 3: WhatsApp CLI<\/h1>\n<p>RUN curl -L https:\/\/github.com\/steipete\/wacli\/releases\/latest\/download\/wacli_Linux_x86_64.tar.gz<br \/>\n  | tar -xz -C \/usr\/local\/bin &amp;&amp; chmod +x \/usr\/local\/bin\/wacli<\/p>\n<h1>Add more binaries below using the same pattern<\/h1>\n<p>WORKDIR \/app<br \/>\nCOPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc .\/<br \/>\nCOPY ui\/package.json .\/ui\/package.json<br \/>\nCOPY scripts .\/scripts<\/p>\n<p>RUN corepack enable<br \/>\nRUN pnpm install &#8211;frozen-lockfile<\/p>\n<p>COPY . .<br \/>\nRUN pnpm build<br \/>\nRUN pnpm ui:install<br \/>\nRUN pnpm ui:build<\/p>\n<p>ENV NODE_ENV=production<\/p>\n<p>CMD [&#8220;node&#8221;,&#8221;dist\/index.js&#8221;]<\/p>\n<pre><code>\n&lt;Note&gt;\n  The download URLs above are for x86_64 (amd64). For ARM-based VMs (e.g. Hetzner ARM, GCP Tau T2A), replace the download URLs with the appropriate ARM64 variants from each tool's release page.\n&lt;\/Note&gt;\n\n## Build and launch\n\n```bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}\ndocker compose build\ndocker compose up -d openclaw-gateway\n<\/code><\/pre>\n<p>If build fails with <code>Killed<\/code> or <code>exit code 137<\/code> during <code>pnpm install --frozen-lockfile<\/code>, the VM is out of memory.<br \/>\nUse a larger machine class before retrying.<\/p>\n<p>Verify binaries:<\/p>\n<p>&#8220;`bash  theme={&#8220;theme&#8221;:{&#8220;light&#8221;:&#8221;min-light&#8221;,&#8221;dark&#8221;:&#8221;min-dark&#8221;}}<br \/>\ndocker compose exec openclaw-gateway which gog<br \/>\ndocker compose exec openclaw-gateway which goplaces<br \/>\ndocker compose exec openclaw-gateway which wacli<\/p>\n<pre><code>\nExpected output:\n\n<\/code><\/pre>\n<p>\/usr\/local\/bin\/gog<br \/>\n\/usr\/local\/bin\/goplaces<br \/>\n\/usr\/local\/bin\/wacli<\/p>\n<pre><code>\nVerify Gateway:\n\n```bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}\ndocker compose logs -f openclaw-gateway\n<\/code><\/pre>\n<p>Expected output:<\/p>\n<pre><code>[gateway] listening on ws:\/\/0.0.0.0:18789\n<\/code><\/pre>\n<h2>What persists where<\/h2>\n<p>OpenClaw runs in Docker, but Docker is not the source of truth.<br \/>\nAll long-lived state must survive restarts, rebuilds, and reboots.<\/p>\n<table>\n<thead>\n<tr>\n<th>Component<\/th>\n<th>Location<\/th>\n<th>Persistence mechanism<\/th>\n<th>&#35828;&#26126;s<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Gateway config<\/td>\n<td><code>\/home\/node\/.openclaw\/<\/code><\/td>\n<td>Host volume mount<\/td>\n<td>Includes <code>openclaw.json<\/code>, tokens<\/td>\n<\/tr>\n<tr>\n<td>Model auth profiles<\/td>\n<td><code>\/home\/node\/.openclaw\/<\/code><\/td>\n<td>Host volume mount<\/td>\n<td>OAuth tokens, API keys<\/td>\n<\/tr>\n<tr>\n<td>Skill configs<\/td>\n<td><code>\/home\/node\/.openclaw\/skills\/<\/code><\/td>\n<td>Host volume mount<\/td>\n<td>Skill-level state<\/td>\n<\/tr>\n<tr>\n<td>Agent workspace<\/td>\n<td><code>\/home\/node\/.openclaw\/workspace\/<\/code><\/td>\n<td>Host volume mount<\/td>\n<td>Code and agent artifacts<\/td>\n<\/tr>\n<tr>\n<td>WhatsApp session<\/td>\n<td><code>\/home\/node\/.openclaw\/<\/code><\/td>\n<td>Host volume mount<\/td>\n<td>Preserves QR login<\/td>\n<\/tr>\n<tr>\n<td>Gmail keyring<\/td>\n<td><code>\/home\/node\/.openclaw\/<\/code><\/td>\n<td>Host volume + password<\/td>\n<td>Requires <code>GOG_KEYRING_PASSWORD<\/code><\/td>\n<\/tr>\n<tr>\n<td>External binaries<\/td>\n<td><code>\/usr\/local\/bin\/<\/code><\/td>\n<td>Docker image<\/td>\n<td>Must be baked at build time<\/td>\n<\/tr>\n<tr>\n<td>Node runtime<\/td>\n<td>Container filesystem<\/td>\n<td>Docker image<\/td>\n<td>Rebuilt every image build<\/td>\n<\/tr>\n<tr>\n<td>OS packages<\/td>\n<td>Container filesystem<\/td>\n<td>Docker image<\/td>\n<td>Do not install at runtime<\/td>\n<\/tr>\n<tr>\n<td>Docker container<\/td>\n<td>Ephemeral<\/td>\n<td>Restartable<\/td>\n<td>Safe to destroy<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Updates<\/h2>\n<p>To update OpenClaw on the VM:<\/p>\n<p><code>bash  theme={\"theme\":{\"light\":\"min-light\",\"dark\":\"min-dark\"}}<br \/>\ngit pull<br \/>\ndocker compose build<br \/>\ndocker compose up -d<\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Docker VM Runtime Shared runtime steps for VM-based Doc [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-616","post","type-post","status-publish","format-standard","hentry","category-docs"],"_links":{"self":[{"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/posts\/616","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/comments?post=616"}],"version-history":[{"count":3,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/posts\/616\/revisions"}],"predecessor-version":[{"id":770,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/posts\/616\/revisions\/770"}],"wp:attachment":[{"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/media?parent=616"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/categories?post=616"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/tags?post=616"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}