在 Zeabur 部署應用時出現 exec format error、no such file or directory 或 symbol not found?本文詳解這些常見 Linux 錯誤的根本原因(如架構或 libc 不相容),並提供正確 Dockerfile 與 build 設定範例,幫助你一次搞懂 musl / glibc 差異與解法。
Written by: Chia1104 CC BY-NC-SA 4.0
在 Zeabur 部署時,容器啟動會出現:
exec ./server: no such file or directory - 檔案明明存在,卻說找不到exec ./server: exec format error - 可執行檔格式/架構不符合symbol not found - 動態連結的符號解析失敗,常見於 libc/loader 不相容exec ./server: no such file or directory為什麼會出現 exec ./server: no such file or directory(但檔案明明存在)
這個錯誤很多人直覺以為是檔案不見,其實常見原因是 libc/loader 不相容:
這個錯誤最容易誤判成「COPY 沒成功」或「路徑錯」,但在 Linux 上,即使 binary 檔案存在,只要它是 動態連結 ELF,而且 ELF 內指定的 interpreter(loader,例如 ld-linux...)在當前系統不存在,就可能回報 No such file or directory。
實務上最常見的觸發方式之一,就是 builder 跟 runner 使用不同 libc:例如在 Alpine(musl)環境產生的東西,拿去 glibc 環境跑(或反過來),導致 loader 路徑或相依庫不吻合,最後用「看起來像檔案不見」的方式失敗。
exec ./server: exec format error:二進位架構不符這通常表示你把不同 CPU 架構的可執行檔拿來跑,例如 amd64 的環境嘗試執行 arm64 的 binary,就會出現 exec format error。
所以這個錯誤的第一優先檢查點是:你建出來的檔案到底是 amd64 還是 arm64,以及 runner 實際跑在哪個架構上。
symbol not found:動態連結對不上(常見於 libc 差異)symbol not found 多半跟 動態連結有關:程式啟動時需要載入某些 shared library 或解析某些符號,但目前系統提供的版本不符合,於是就爆掉。
在 Alpine(musl)與 Debian/Ubuntu(glibc)之間切換時,這類問題尤其常見,因為相依的 loader/library 生態就是不同。
關鍵原則很簡單:build 產出的 target,要跟 runner 的作業系統/libc 一致,才能穩定執行。
build 產出 amd64 + musl 版本 runner 也使用 alpine(musl)
這樣架構與 libc 都一致,就可以在 Zeabur 正常啟動。
{
"scripts": {
"build:amd64-musl": "bun build --compile --target bun-linux-x64-musl --minify-whitespace --minify-syntax --outfile .output/server src/server.ts",
"start": ".output/server"
}
}ARG NODE_VERSION=24
ARG TARGETARCH=amd64
FROM --platform=linux/${TARGETARCH} node:${NODE_VERSION}-alpine AS builder-base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
ENV TURBO_TELEMETRY_DISABLED=1
WORKDIR /app
RUN apk update && \
apk add --no-cache \
build-base \
gcc \
libc6-compat && \
npm install -g corepack@latest turbo bun && \
corepack enable pnpm
FROM builder-base AS pre-builder
COPY . .
RUN turbo prune --scope=auth-service --docker
FROM builder-base AS builder
COPY --from=pre-builder /app/out/json/ .
COPY --from=pre-builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
COPY --from=pre-builder /app/out/full/ .
COPY turbo.json turbo.json
ARG TURBO_TEAM \
TURBO_TOKEN
ENV TURBO_TEAM=$TURBO_TEAM \
TURBO_TOKEN=$TURBO_TOKEN
RUN pnpm i
RUN pnpm -C services/auth build:amd64-musl
FROM --platform=linux/${TARGETARCH} alpine AS runner
RUN apk update && \
apk add --no-cache \
libstdc++
WORKDIR /app
COPY --from=builder /app/services/auth/.output/server server
ENV NODE_ENV=production \
PORT=8080
EXPOSE 8080
CMD ["./server"]