
如果你也遇到過這種情況——拿到一臺(tái)完全隔離的內(nèi)網(wǎng)服務(wù)器要求在它上面把 Openclaw 服務(wù)部署起來而整個(gè)環(huán)境既沒有外網(wǎng)訪問權(quán)限也沒有可用的軟件源鏡像那這篇方案應(yīng)該能幫到你。這套 Openclaw 2026.3.7 Docker 離線部署方案是我在實(shí)際交付過程中整理出來的。核心就四件事在聯(lián)網(wǎng)機(jī)器上做好鏡像打包、把離線包安全搬進(jìn)內(nèi)網(wǎng)、在內(nèi)網(wǎng)完成加載與編排、最后做一套可重復(fù)的功能驗(yàn)證。先把這個(gè)方案解決的核心問題說清楚。在線部署的時(shí)候命令基本就三條拉鏡像、寫 compose、啟動(dòng)。到了內(nèi)網(wǎng)環(huán)境這三條全都會(huì)被卡住。沒有軟件源裝不了環(huán)境依賴沒有鏡像倉(cāng)庫(kù)訪問權(quán)限拉不了鏡像如果連 DNS 都不通甚至連外網(wǎng)地址都解析不了。更麻煩的是Openclaw 這類服務(wù)通常不是一個(gè)容器就能跑起來的它至少要依賴一個(gè)數(shù)據(jù)庫(kù)實(shí)例和一個(gè)緩存實(shí)例也就是說你需要的不是一個(gè)鏡像而是一整組鏡像。我在動(dòng)手前給自己定了三條驗(yàn)收標(biāo)準(zhǔn)這三條標(biāo)準(zhǔn)決定了后面所有打包和編排的細(xì)節(jié)。第一交付物必須能在全新服務(wù)器上按文檔從零復(fù)現(xiàn)。也就是說只要拿到這套離線包再跟著文檔操作即使執(zhí)行的人沒有參與過最初構(gòu)建也能完成部署。這意味著所有命令、所有鏡像、所有配置都必須是完整的不存在到時(shí)候再查一下這種懸空環(huán)節(jié)。第二交付物必須是一個(gè)獨(dú)立目錄而不是一堆聊天記錄里的命令。離線部署的時(shí)間窗口通常很短要是臨時(shí)發(fā)現(xiàn)少了某個(gè)鏡像或者配置文件很難再回外網(wǎng)補(bǔ)一次。第三整個(gè)部署過程不能依賴任何外網(wǎng)資源。包括鏡像倉(cāng)庫(kù)、軟件源、公共 DNS統(tǒng)統(tǒng)不能依賴。只有把這三條標(biāo)準(zhǔn)想清楚后面的結(jié)構(gòu)才不會(huì)走偏。我最終交付的離線包目錄大概長(zhǎng)這樣openclaw-offline-2026.3.7/ ├── images/ │ ├── openclaw-2026.3.7.tar.gz │ ├── postgres-15.4-alpine.tar.gz │ ├── redis-7.2-alpine.tar.gz │ └── sha256sums.txt ├── config/ │ ├── docker-compose.yml │ ├── .env.example │ └── deployment.md └── scripts/ ├── 01-load-images.sh ├── 02-import-registry.sh └── 03-verify.shimages 目錄裝的是所有鏡像的歸檔文件config 目錄裝的是編排文件和環(huán)境變量模板scripts 目錄把重復(fù)性操作固化成腳本。這個(gè)結(jié)構(gòu)不一定適合所有項(xiàng)目但如果你也在做類似交付可以參考這個(gè)思路鏡像與配置分離、配置與腳本分離、文檔與操作分離。1. 離線部署這件事難點(diǎn)到底在哪兒很多人在剛開始做離線部署時(shí)會(huì)有一個(gè)誤解覺得離線部署不過是把在線安裝的步驟照抄一遍只是把拉鏡像換成了導(dǎo)入鏡像包。但實(shí)際跑一遍就會(huì)發(fā)現(xiàn)在線環(huán)境里大量默認(rèn)就有的能力到了內(nèi)網(wǎng)全部要靠自己補(bǔ)齊。1.1 在線部署的順手恰恰是離線部署最大的障礙在線環(huán)境里你根本不需要考慮鏡像從哪來docker pull 直接解決不需要考慮容器啟動(dòng)時(shí)要不要聯(lián)網(wǎng)宿主機(jī)的網(wǎng)絡(luò)環(huán)境天然可用不需要考慮 DNS 解析問題公共 DNS 就在那。但內(nèi)網(wǎng)環(huán)境把這些便利全部拿掉之后你才發(fā)現(xiàn)一條 docker compose up -d 背后藏了多少隱式依賴。Openclaw 這種服務(wù)更明顯。它啟動(dòng)時(shí)要連數(shù)據(jù)庫(kù)、連緩存這些依賴服務(wù)的鏡像也得一起準(zhǔn)備好。更不起眼的是很多鏡像構(gòu)建時(shí)基于的基礎(chǔ)鏡像比如 alpine、debian-slim一旦缺失整個(gè)鏡像就無法加載。所以離線部署的第一難點(diǎn)不是Docker 怎么用而是你要把所有隱式依賴提前暴露出來并一一打包。1.2 我給自己定的三條驗(yàn)收標(biāo)準(zhǔn)這次做完 Openclaw 2026.3.7 的離線包之后我把整個(gè)部署流程沉淀成了一套固定動(dòng)作。以后不管換到哪臺(tái)內(nèi)網(wǎng)機(jī)器執(zhí)行節(jié)奏都是一樣的校驗(yàn)離線包完整性加載鏡像或者推送到內(nèi)網(wǎng)倉(cāng)庫(kù)調(diào)整 compose 文件和環(huán)境變量啟動(dòng)并跑驗(yàn)證清單。這四條按順序下來中間不需要臨時(shí)查文檔也不需要臨時(shí)摸去外網(wǎng)補(bǔ)鏡像。三條驗(yàn)收標(biāo)準(zhǔn)里我最看重第一條可復(fù)現(xiàn)。因?yàn)殡x線部署一旦出錯(cuò)排查窗口往往很短你不希望到現(xiàn)場(chǎng)才發(fā)現(xiàn)交付物缺胳膊少腿。1.3 鏡像歸集與配置分離在交付目錄里我把 images 和 config 拆開是有意為之的。鏡像歸檔是二進(jìn)制文件體積大、基本不會(huì)變動(dòng)compose 文件和 .env 是文本文件會(huì)隨著不同內(nèi)網(wǎng)環(huán)境的網(wǎng)絡(luò)規(guī)劃、資源限制而調(diào)整。把兩者分離之后同一個(gè)鏡像包可以重復(fù)使用不用為每臺(tái)服務(wù)器重新打一次包。另外我還放了一個(gè) scripts 目錄專門放加載腳本和校驗(yàn)?zāi)_本。原因是人工敲命令最容易出錯(cuò)尤其在連續(xù)操作好幾個(gè) G 的鏡像包時(shí)。腳本化之后執(zhí)行人只需要按編號(hào)一步步跑每一步都有明確的輸出出錯(cuò)時(shí)也能直接定位到是加載問題還是網(wǎng)絡(luò)問題。2. 打包階段聯(lián)網(wǎng)機(jī)器上需要完成的準(zhǔn)備工作離線部署的第一步是在一臺(tái)能正常訪問外網(wǎng)的機(jī)器上把鏡像準(zhǔn)備好。這個(gè)階段看起來簡(jiǎn)單實(shí)際最容易埋雷。鏡像架構(gòu)、導(dǎo)出命令、依賴清單任何一個(gè)環(huán)節(jié)漏掉到了內(nèi)網(wǎng)都會(huì)變成數(shù)小時(shí)起步的排查時(shí)間。2.1 先確認(rèn)目標(biāo)服務(wù)器的 CPU 架構(gòu)打包之前必須知道內(nèi)網(wǎng)服務(wù)器的 CPU 架構(gòu)。這一步?jīng)Q定了你要拉取的是 x86_64 鏡像還是 arm64 鏡像。查詢命令很簡(jiǎn)單docker image inspect openclaw/openclaw:2026.3.7 --format {{.Architecture}}如果你是在 x86 機(jī)器上打包但內(nèi)網(wǎng)服務(wù)器是 ARM 架構(gòu)直接用默認(rèn)參數(shù) pull 出來的鏡像load 進(jìn)內(nèi)網(wǎng)后大概率啟動(dòng)不了或者報(bào)錯(cuò)報(bào)得莫名其妙。拉取時(shí)可以顯式指定平臺(tái)docker pull --platform linux/arm64 openclaw/openclaw:2026.3.7指定了--platform之后docker save 出來的鏡像歸檔里架構(gòu)字段就是linux/arm64load 到 ARM 服務(wù)器上才能正常工作。我見過有同事在打包時(shí)沒有指定平臺(tái)結(jié)果所有鏡像都按 x86 拉取到了 ARM 服務(wù)器上只有基礎(chǔ)鏡像能跑應(yīng)用鏡像全都報(bào)exec format error日志里又看不出任何配置問題。還有一個(gè)很容易被忽略的點(diǎn)打包機(jī)器的 Docker 版本最好不低于內(nèi)網(wǎng)服務(wù)器的 Docker 版本。雖然鏡像格式有向后兼容的設(shè)計(jì)但比較新的鏡像層壓縮格式在很老版本的 Docker 上 load 時(shí)可能遇到unknown compression algorithm這類報(bào)錯(cuò)。穩(wěn)妥的做法是提前確認(rèn)兩邊 Docker 版本差距不要太大。2.2 用 docker save 而不是 docker export這一步是我最想強(qiáng)調(diào)的細(xì)節(jié)。導(dǎo)出鏡像有兩種命令docker save和docker export很多人會(huì)把它們弄混。docker save導(dǎo)出的是鏡像本身的完整結(jié)構(gòu)包括每一層、環(huán)境變量、入口命令、標(biāo)簽信息docker export導(dǎo)出的是某個(gè)容器的文件系統(tǒng)它會(huì)丟失鏡像的歷史層和元數(shù)據(jù)。離線部署必須用docker save因?yàn)閷?dǎo)入之后你仍然要以鏡像的方式運(yùn)行而不是以裸文件系統(tǒng)的方式運(yùn)行。實(shí)際操作中我習(xí)慣用歸檔加壓縮的方式一條命令完成docker save openclaw/openclaw:2026.3.7 | gzip images/openclaw-2026.3.7.tar.gz壓縮的好處是傳輸體積能明顯降下來。如果鏡像很大歸檔文件有幾個(gè) G 甚至十幾個(gè) G還可以再拆成多卷split -b 4G -d openclaw-2026.3.7.tar.gz openclaw-2026.3.7.tar.gz_到了內(nèi)網(wǎng)再合并cat openclaw-2026.3.7.tar.gz_* openclaw-2026.3.7.tar.gz分卷和合并本身不復(fù)雜真正需要養(yǎng)成習(xí)慣的是在傳輸前計(jì)算校驗(yàn)值。用 sha256sum 生成一份清單隨鏡像包一起帶到內(nèi)網(wǎng)sha256sum images/*.tar.gz images/sha256sums.txt數(shù)據(jù)包在內(nèi)網(wǎng)傳輸過程中經(jīng)歷過移動(dòng)硬盤拷貝、網(wǎng)絡(luò)共享、其他中轉(zhuǎn)方式之后是否完整其實(shí)是不確定的。我遇到過歸檔文件在傳輸中少了一個(gè)分卷加載時(shí) Docker 也不報(bào)明顯錯(cuò)誤最后是啟動(dòng)階段才暴露出缺層的詭異問題。有了校驗(yàn)清單加上一個(gè)核對(duì)步驟這類問題就能提前擋掉。2.3 依賴鏡像一個(gè)都不能少Openclaw 這類服務(wù)很少是單個(gè)容器獨(dú)立運(yùn)行的。我這次部署的版本就依賴一個(gè) PostgreSQL 數(shù)據(jù)庫(kù)實(shí)例和一個(gè) Redis 緩存實(shí)例。如果你在聯(lián)網(wǎng)環(huán)境下已經(jīng)用 docker compose 跑通過可以直接用這條命令把 compose 里實(shí)際引用的鏡像完整展開docker compose config --images這條命令比人工一個(gè)個(gè)翻配置文件可靠得多。列出來之后逐一執(zhí)行 docker save 歸檔。還有一個(gè)容易漏的點(diǎn)compose 里可能引用了輔助容器比如初始化數(shù)據(jù)庫(kù)的 migrate 容器這類容器只在啟動(dòng)時(shí)運(yùn)行一段腳本如果漏掉它后面 Openclaw 主服務(wù)啟動(dòng)時(shí)會(huì)一直報(bào)連不上數(shù)據(jù)庫(kù)。打包階段的最后用docker images檢查所有鏡像是否已經(jīng)在本地。確認(rèn)沒有遺漏后再進(jìn)入下一步。寧可在這里多花十分鐘核對(duì)清單也不要等到了內(nèi)網(wǎng)才發(fā)現(xiàn)缺這個(gè)缺那個(gè)。3. 鏡像進(jìn)內(nèi)網(wǎng)的兩種主流路徑與我的選型取舍鏡像打包完之后核心問題就是如何把它們弄進(jìn)隔離的服務(wù)器。通常有兩條路用 docker load 直接加載歸檔或者在內(nèi)網(wǎng)部署一個(gè)私有鏡像倉(cāng)庫(kù)再把鏡像推上去。3.1 路徑 Adocker load適合少量節(jié)點(diǎn)最簡(jiǎn)單直接的方式把歸檔文件拷貝到內(nèi)網(wǎng)服務(wù)器上然后一條命令加載docker load -i images/openclaw-2026.3.7.tar.gz加載完成后用docker images確認(rèn)鏡像名稱和標(biāo)簽。這里有個(gè)常見問題docker save 歸檔里保存的是打包時(shí)的完整鏡像名和標(biāo)簽。如果內(nèi)網(wǎng)里你需要改成私有倉(cāng)庫(kù)地址的命名加載后得再打一次 tagdocker tag openclaw/openclaw:2026.3.7 192.168.1.100:5000/openclaw/openclaw:2026.3.7docker load 適合鏡像數(shù)量少、節(jié)點(diǎn)數(shù)量也少的場(chǎng)景。比如單機(jī)部署或者最多兩三臺(tái)機(jī)器直接 load 就夠了不需要額外考慮鏡像倉(cāng)庫(kù)的維護(hù)問題。3.2 路徑 B私有鏡像倉(cāng)庫(kù)適合規(guī)?;h(huán)境如果內(nèi)網(wǎng)節(jié)點(diǎn)很多一臺(tái)一臺(tái) load 顯然不現(xiàn)實(shí)。這種場(chǎng)景下我會(huì)在隔離環(huán)境里先部署一個(gè)私有鏡像倉(cāng)庫(kù)然后把離線包里的鏡像全部 push 進(jìn)去。最簡(jiǎn)單的方式是直接用 registry 容器docker run -d --name registry --restartalways -p 5000:5000 -v /opt/registry:/var/lib/registry registry:2注意registry 這個(gè)容器本身的鏡像也需要提前打包進(jìn)離線包里否則內(nèi)網(wǎng)環(huán)境同樣拉取不到。推送前把鏡像重新打標(biāo)簽改成內(nèi)網(wǎng)倉(cāng)庫(kù)地址docker tag openclaw/openclaw:2026.3.7 192.168.1.100:5000/openclaw/openclaw:2026.3.7 docker push 192.168.1.100:5000/openclaw/openclaw:2026.3.7這里有一個(gè)內(nèi)網(wǎng)環(huán)境非常典型的攔截點(diǎn)如果你的私有鏡像倉(cāng)庫(kù)沒有配置 TLS 證書Docker 默認(rèn)會(huì)拒絕通過 HTTP 方式推送。解決辦法是在內(nèi)網(wǎng)每臺(tái)節(jié)點(diǎn)的/etc/docker/daemon.json里配置 insecure-registries{ insecure-registries: [192.168.1.100:5000] }修改完 daemon 配置后需要重啟 Dockersystemctl restart docker如果你所在團(tuán)隊(duì)已經(jīng)有現(xiàn)成的鏡像倉(cāng)庫(kù)那就更簡(jiǎn)單了直接把離線包推到已有倉(cāng)庫(kù)即可不需要額外部署一個(gè)新的。3.3 兩種路徑可以同時(shí)保留我最后交付的方案里兩條路徑都保留了。為什么因?yàn)椴煌瑑?nèi)網(wǎng)環(huán)境的現(xiàn)狀差異很大。有的團(tuán)隊(duì)已經(jīng)維護(hù)了自己的鏡像倉(cāng)庫(kù)離線包只需要推到已有倉(cāng)庫(kù)有的環(huán)境則什么組件都不讓裝registry 這種新容器可能還要走額外的審批流程這時(shí)直接 load 反而更省事。所以我只是把 01-load-images.sh 和 02-import-registry.sh 兩個(gè)腳本都放進(jìn)交付目錄并寫明各自的適用場(chǎng)景。使用者按自己的內(nèi)網(wǎng)條件選一條不會(huì)因?yàn)闆]有某個(gè)組件而被卡死。這里也提醒一句無論走哪條路徑鏡像打好 tag 并推送到倉(cāng)庫(kù)之后原始?xì)w檔文件不要急著刪留到驗(yàn)證完成后再處理。因?yàn)槿绻麄}(cāng)庫(kù)臨時(shí)出問題至少還能重新 load。4. compose 啟動(dòng)之前內(nèi)網(wǎng)特有的配置調(diào)整點(diǎn)鏡像就位只是第一步真正決定部署能否一次成功的是 compose 編排文件里的那些細(xì)節(jié)。很多在線環(huán)境里不是問題的配置到了內(nèi)網(wǎng)全部變成必須處理的坑。這一段我按實(shí)際使用順序來寫你照著一項(xiàng)項(xiàng)核對(duì)基本能避開大多數(shù)坑。4.1 顯式關(guān)閉自動(dòng)拉取和隱式依賴在線部署時(shí)docker compose up -d 會(huì)自動(dòng)去遠(yuǎn)程倉(cāng)庫(kù)拉取缺失的鏡像。內(nèi)網(wǎng)環(huán)境沒有這個(gè)能力所以在 compose 文件里盡量顯式聲明pull_policy: never強(qiáng)制 Docker 只使用本地已經(jīng)存在的鏡像。否則 compose 仍然會(huì)嘗試訪問遠(yuǎn)程倉(cāng)庫(kù)連接不通時(shí)整段啟動(dòng)過程可能卡在拉取階段半天不報(bào)錯(cuò)。我這次使用的 compose 文件結(jié)構(gòu)大致如下services: postgres: image: 192.168.1.100:5000/postgres/postgres:15.4-alpine environment: POSTGRES_DB: openclaw POSTGRES_USER: openclaw POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - pg_data:/var/lib/postgresql/data healthcheck: test: [CMD-SHELL, pg_isready -U openclaw -d openclaw] interval: 10s timeout: 5s retries: 5 networks: - openclaw_net redis: image: 192.168.1.100:5000/redis/redis:7.2-alpine healthcheck: test: [CMD, redis-cli, ping] interval: 10s timeout: 5s retries: 5 networks: - openclaw_net openclaw: image: 192.168.1.100:5000/openclaw/openclaw:2026.3.7 pull_policy: never depends_on: postgres: condition: service_healthy redis: condition: service_healthy ports: - 8080:8080 environment: OPENCLAW_DB_URL: postgresql://openclaw:${POSTGRES_PASSWORD}postgres:5432/openclaw OPENCLAW_REDIS_URL: redis://redis:6379/0 OPENCLAW_LOG_LEVEL: info volumes: - openclaw_data:/opt/openclaw/data - openclaw_config:/opt/openclaw/config healthcheck: test: [CMD, curl, -f, http://127.0.0.1:8080/health] interval: 30s timeout: 10s retries: 5 start_period: 40s networks: - openclaw_net volumes: pg_data: openclaw_data: openclaw_config: networks: openclaw_net: driver: bridge提示上面的環(huán)境變量名、健康檢查路徑是我在實(shí)際版本里寫出來的典型形態(tài)。不同版本的 Openclaw 命名可能有差異拿到鏡像后先跑一次docker history或者docker inspect確認(rèn)真正的環(huán)境變量名再套用文件。4.2 健康檢查決定啟動(dòng)順序不只是好習(xí)慣很多人部署時(shí)不加 healthcheck因?yàn)樵诰€環(huán)境下docker compose 默認(rèn)的啟動(dòng)順序基本夠用。但如果數(shù)據(jù)庫(kù)或緩存還沒就緒就讓 Openclaw 啟動(dòng)它會(huì)在啟動(dòng)階段反復(fù)重試連接嚴(yán)重時(shí)會(huì)造成初始化失敗。所以我在依賴服務(wù)上都加了 healthcheck并在 Openclaw 上用 condition 語法等待它們健康后再啟動(dòng)。這個(gè)寫法的效果是只有當(dāng) PostgreSQL 返回pg_isready成功、Redis 返回PONG之后Openclaw 才會(huì)被拉起。需要注意的是condition: service_healthy這種寫法需要 Docker Compose v2 支持如果你的內(nèi)網(wǎng)服務(wù)器還在用很早期的 docker-compose v1可能需要先升級(jí)或者改用一個(gè)等待腳本。start_period參數(shù)也別省。Openclaw 這類服務(wù)啟動(dòng)時(shí)可能要做配置解析、建表、遷移前幾十秒探活失敗是正常的。沒有 start_period 的話健康檢查會(huì)按固定間隔重試一旦超過 retries 次數(shù)就標(biāo)記為不健康哪怕服務(wù)只是啟動(dòng)慢了一點(diǎn)。4.3 數(shù)據(jù)持久化、日志輪轉(zhuǎn)和 .env 管理離線環(huán)境里沒有云廠商的后置存儲(chǔ)數(shù)據(jù)一旦丟失恢復(fù)成本非常高。所以數(shù)據(jù)庫(kù)和主服務(wù)的數(shù)據(jù)目錄必須用命名卷或綁定掛載落到宿主機(jī)。我上面寫的是命名卷方案實(shí)際部署時(shí)也可以用宿主機(jī)路徑/opt/openclaw/pgdata:/var/lib/postgresql/data /opt/openclaw/appdata:/opt/openclaw/data日志也得提前想好。容器默認(rèn)的 json-file 日志會(huì)無限增長(zhǎng)內(nèi)網(wǎng)磁盤空間本來就緊張建議顯式配置輪轉(zhuǎn)logging: driver: json-file options: max-size: 20m max-file: 5還有一個(gè)容易被忽略的問題密碼和敏感配置不能直接寫死在 compose 文件里。我在交付包里只放.env.example不填真實(shí)密碼。部署時(shí)復(fù)制一份.env填入符合內(nèi)網(wǎng)要求的強(qiáng)密碼。好處是交付包可以反復(fù)流轉(zhuǎn)不會(huì)帶出敏感憑據(jù)。5. 啟動(dòng)之后的驗(yàn)證標(biāo)準(zhǔn)與回滾預(yù)案離線部署不是容器起來了就算成功它是一門需要驗(yàn)收標(biāo)準(zhǔn)的工程。我把部署完成后的驗(yàn)證項(xiàng)整理成了一張清單每次部署結(jié)束之后照著跑一遍能省掉后面很多折騰。檢查項(xiàng)操作期望結(jié)果容器狀態(tài)docker compose ps所有服務(wù) UpOpenclaw 顯示 healthy健康檢查接口curl -fsS http://127.0.0.1:8080/health返回 HTTP 200主應(yīng)用日志docker compose logs --tail200 openclaw無連續(xù)的 Error 級(jí)別日志數(shù)據(jù)庫(kù)連接docker compose exec postgres pg_isready -U openclaw返回 accepting connections數(shù)據(jù)落盤docker volume inspect openclaw_openclaw_dataMountpoint 目錄存在內(nèi)網(wǎng)訪問從內(nèi)網(wǎng)另一臺(tái)機(jī)器訪問服務(wù)地址頁面或接口可以正常打開每一項(xiàng)都有意義而不是走過場(chǎng)。容器狀態(tài)只能證明進(jìn)程在跑健康檢查接口證明服務(wù)真的響應(yīng)了數(shù)據(jù)庫(kù)連接證明主服務(wù)和數(shù)據(jù)庫(kù)之間的鏈路通了內(nèi)網(wǎng)訪問證明端口映射和網(wǎng)絡(luò)策略都沒問題。5.1 回滾預(yù)案數(shù)據(jù)卷不動(dòng)鏡像換一下一旦部署后出現(xiàn)線上問題回滾的第一原則是數(shù)據(jù)卷不動(dòng)鏡像換一下。因?yàn)?Openclaw 的數(shù)據(jù)基本都存在命名卷里回滾只是把 compose 文件里的 image 標(biāo)簽切回舊版本再 up 一次。前提是舊版本鏡像在內(nèi)網(wǎng)里還有保留。我建議打包時(shí)把舊版本鏡像也一并導(dǎo)入原因就在這。有一個(gè)很關(guān)鍵的坑千萬別順手執(zhí)行docker compose down -v。-v參數(shù)會(huì)把命名卷一并刪掉數(shù)據(jù)一旦刪除回滾就無從談起。日常維護(hù)只需要docker compose down不帶任何刪除數(shù)據(jù)參數(shù)。5.2 平滑升級(jí)的固定步驟如果要升級(jí)到下一個(gè)版本我的操作順序是固定的備份數(shù)據(jù)卷或者至少把數(shù)據(jù)目錄完整拷貝一份在聯(lián)網(wǎng)機(jī)器上拉取新版本鏡像導(dǎo)出歸檔傳輸?shù)絻?nèi)網(wǎng)load 并 push 進(jìn)鏡像倉(cāng)庫(kù)修改 compose 文件里的 image 標(biāo)簽重新 up跑一遍驗(yàn)證清單。這六步里最容易忽略的是第一步。備份動(dòng)作一定要獨(dú)立寫一份命令文檔跟著執(zhí)行不要靠記憶。很多線上事故都是升級(jí)前沒做備份結(jié)果配置遷移失敗后進(jìn)退兩難。6. 幾個(gè)差點(diǎn)讓我翻車的細(xì)節(jié)問題前面梳理的都是主線流程最后聊幾個(gè)實(shí)際操作中真真切切踩過的坑。每一個(gè)都耽誤過不少時(shí)間整理出來給后來人提個(gè)醒。6.1 架構(gòu)不匹配報(bào)錯(cuò)和原因?qū)Σ簧线@是離線部署里最隱蔽的問題之一。在 x86 機(jī)器上打包內(nèi)網(wǎng)服務(wù)器卻可能是 ARM 架構(gòu)。如果一開始不知道這個(gè)情況啟動(dòng)后的常見現(xiàn)象是容器反復(fù)重啟日志里只有一行exec format error。這個(gè)報(bào)錯(cuò)和鏡像損壞、配置錯(cuò)誤的表面現(xiàn)象完全不同很容易讓人往配置文件方向排查實(shí)際上原因只是架構(gòu)不一致。排查命令很簡(jiǎn)單docker image inspect openclaw/openclaw:2026.3.7 --format {{.Architecture}}如果在打包機(jī)器上顯示amd64而目標(biāo)服務(wù)器是 ARM就必須重新用--platform linux/arm64拉取后再打包。6.2 健康檢查接口不要死抄模板我把健康檢查地址寫成了 /health但很多服務(wù)的實(shí)際健康檢查接口并不是這個(gè)路徑。有的叫 /status有的根本沒有健康檢查接口。這種情況下健康檢查不能寫 HTTP 探測(cè)可以退而求其次用進(jìn)程檢查比如檢查主進(jìn)程是否存活healthcheck: test: [CMD-SHELL, pgrep -f openclaw || exit 1] interval: 30s timeout: 10s retries: 3原則很簡(jiǎn)單健康檢查必須對(duì)應(yīng)服務(wù)自身的真實(shí)探活能力。寧可保守一點(diǎn)也不要寫一個(gè)永遠(yuǎn)失敗的探活腳本。6.3 容器時(shí)間不同步引發(fā)的詭異問題這是一個(gè)藏在鏡像里的坑。容器繼承的是宿主機(jī)內(nèi)核時(shí)間如果宿主機(jī)時(shí)間不對(duì)容器內(nèi)的服務(wù)可能不會(huì)立刻報(bào)錯(cuò)但在日志時(shí)間戳、緩存過期、賬號(hào)有效期這些場(chǎng)景里會(huì)表現(xiàn)出間歇性的異常。內(nèi)網(wǎng)服務(wù)器如果長(zhǎng)期沒有時(shí)間同步策略時(shí)間偏差可能達(dá)到幾分鐘甚至更長(zhǎng)。部署前先把宿主機(jī)的時(shí)鐘校準(zhǔn)一遍至少要在部署文檔里加一條檢查命令date timedatectl status如果內(nèi)網(wǎng)沒有可用的時(shí)間服務(wù)器也要盡量人工校準(zhǔn)到接近真實(shí)時(shí)間否則后續(xù)排查問題的成本會(huì)很高。6.4 大文件傳輸后一定要核對(duì)完整性鏡像歸檔文件動(dòng)輒好幾個(gè) G傳輸過程出問題并不是小概率事件。我遇到過一種情況歸檔文件大小看起來沒少但加載時(shí) Docker 提示某個(gè) layer 不存在后來才發(fā)現(xiàn)是傳輸過程中文件被截?cái)嗔?。從那以后所有歸檔文件都走 sha256 校驗(yàn)流程。生成校驗(yàn)清單在工作目錄里執(zhí)行傳輸?shù)絻?nèi)網(wǎng)后執(zhí)行一次校驗(yàn)確認(rèn)兩邊完全一致后再進(jìn)入 load 或 push 階段。這幾次部署之后我的習(xí)慣是只要執(zhí)行完驗(yàn)證清單就把整個(gè)交付目錄重新歸檔一次命名加上日期和鏡像版本。這樣做的好處是下次任何一個(gè)同事接手都只需要照著同一個(gè)目錄結(jié)構(gòu)操作不需要再來問我當(dāng)時(shí)的命令是什么。離線部署這件事本身不難難的是把每一個(gè)步驟固定成可復(fù)現(xiàn)的流程。希望這份 Openclaw 2026.3.7 Docker 離線部署方案能幫你少走幾步彎路。