🤞

Bun 1.2 の Single-file executable を Docker で動かす方法

2025/02/02に公開

元々 Bun は Bundler としての機能を持っているため import しているファイルを1つのファイルにまとめて実行ファイルにする機能があったのですが、Bun 1.2 でクロスコンパイルに対応したため、ローカルの ARM マシンの MacBook Pro から Linux 向けの実行ファイルを作成できるようになりました。
そのため、マルチステージでわざわざ bun install でファイルを取ってきたりコンパイルしたりする必要はなくなり便利になりました。

https://bun.sh/blog/bun-v1.2#standalone-executables

Standalone executables
You can use bun build --compile to compile your application, and Bun, into a standalone executable.

スタンドアロン実行ファイル
bun build --compile を使用して、アプリケーションと Bun をスタンドアロン実行ファイルにコンパイルできます。Bun 1.2 では、クロスコンパイルのサポートが追加されました。

In Bun 1.2, we've added support for cross-compilation. This allows you to build a Windows or macOS binary on a Linux machine, and vice versa.

これにより、Linux マシンで Windows または macOS バイナリをビルドでき、その逆も可能です。

この記事では作成した実行ファイルから Docker コンテナイメージを作成するところまで説明しています。

環境: MacBook Pro (M2 Pro)

bun build --compile --target=bun-linux-x64-baseline

新規ディレクトリで bun init を実行し、以下のような index.ts ファイルを用意します。

index.ts
Bun.serve({
  hostname: "0.0.0.0",
  fetch: () => new Response("ok"),
})

以下のコマンドで dist/server という実行ファイルを作成します。

bun build ./index.ts --compile --target=bun-linux-x64-baseline --outfile dist/server

Dockerfile

Dockerfile は oven/bun:1 をベースにしてもいいのですが、いらないファイルもそこそこ含まれていたので、筆者の検証の限りは /lib/x86_64-linux-gnu/lib64/ld-linux-x86-64.so.2 だけあればとりあえずは困りませんでした。
利用する機能によってはもっと /lib/x86_64-linux-gnu 内をスリムにできると思いますが、実行ファイルの方が大きいためこれ以上の最適化は行いませんでした。

FROM scratch

COPY --from=oven/bun:1 /lib/x86_64-linux-gnu /lib/x86_64-linux-gnu
COPY --from=oven/bun:1 /lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2

WORKDIR /app

COPY ./dist/server /app/server

CMD ["/app/server"]
docker build --platform=linux/amd64 -t server:latest .

あとは、好きなコンテナプラットフォームで動作させるだけです。

まとめ

なお、ネイティブモジュールを含む場合大丈夫なのか、とかそこまでは検証していないので、利用する際は気をつけてください。

その他の細かいことは、公式ドキュメントを参照してください。

https://bun.sh/docs/bundler/executables

Discussion