💎

devcontainer の ruby feature で Ruby 4.0 がインストールできなかった

に公開

はじめに:最新の Ruby と開発環境のジレンマ

Ruby 4.0 のリリースに、多くの Rubyist が胸を躍らせたのではないでしょうか。新しいバージョンには、パフォーマンスの向上や新機能など、たくさんの魅力が詰まっています。しかし、新しい技術を試す際には、常に「開発環境をどう構築するか」という課題がつきまといます。

特に、プロジェクトごとに異なる Ruby のバージョンを管理したり、チームメンバー間で環境を統一したりするのは、なかなかの手間がかかる作業です。「私のマシンでは動くのに...」といった問題は避けたいものです。

そんな現代の開発環境における課題をスマートに解決してくれるのが、Dev Container です。Docker コンテナ内に開発環境を閉じ込めることで、OS やローカル環境の差異を吸収し、誰でも同じ環境を数コマンドで再現できます。VS Code や Zed などのエディタとの統合も強力で、一度設定すれば、コンテナ内での開発を意識することなく、コーディングに集中できます。

しかし、便利な Dev Container にも思わぬ落とし穴がありました。デファクトスタンダードである https://github.com/devcontainers/features で提供されている Ruby の Dev Container Feature は rvm (Ruby Version Manager) を利用していますが、記事執筆時点(2026年2月4日)では、まだ Ruby 4.0 のような最新バージョンをインストールすることには対応していませんでした。

この記事では、dvc-ruby4-202602 というプロジェクトを例に、この「標準の rvm では Ruby 4.0 がインストールできない」という問題にどう立ち向かったのか、具体的な Dockerfile のカスタマイズ方法を中心に、その解決策と実装プロセスを詳しく解説します。

この記事を読むことで、rvm を使った Ruby のバージョン管理について理解を深めることができます。また、Dev Container 用 Ruby Feature 利用時に、その知識を応用することができるようになります。こういった知識を身につけることで、Ruby 用の開発環境を自在にカスタマイズし、どんなバージョンの Ruby 環境でも構築できるようになれるはずです。それでは、さっそく見ていきましょう。

rvm と Dev Container Ruby Feature の現状

今回のカスタマイズを理解するために、まずは rvm と、それが Dev Container でどのように利用されているかについて少しおさらいしましょう。

rvm (Ruby Version Manager) とは?

rvm (Ruby Version Manager) は、その名の通り、一つのシステム内に複数の Ruby バージョンをインストールし、プロジェクトごとに切り替えて利用できるようにするためのツールです。次のサイトで公開されています。

「プロジェクトAでは Ruby 2.7 が必要だけど、新しいプロジェクトBでは Ruby 3.3 を使いたい」といった場合に、rvm があれば rvm use 2.7.0rvm use 3.3.0 のような簡単なコマンドで、環境を汚さずにバージョンを切り替えられます。Ruby 開発者にとっては、非常に馴染み深いツールの一つです。

最近は、rvm よりも高速な mise-en-place というツールが人気があり、筆者は個人的に ruby のバージョン管理をする場合は、こちらを使っています。

Dev Container の Ruby Feature との関連

この Ruby 開発者に人気のある rvm が、Dev Container とどういった関係があるのでしょうか。

Dev Container には、特定の言語やツールセットを簡単に追加するための Features という仕組みがあります。devcontainer.json に数行書き加えるだけで、例えば Java の開発環境一式をコンテナ内にセットアップできます。

そして、この Features には Ruby Feature という Ruby の開発環境用のものがあり、これが rvm を内部で利用しているのです。

今回のプロジェクトのベースイメージとなっている hiro345g/dvc:ruby-202602 も、この Ruby Feature を利用して構築されており、特定のバージョンの rvm と Ruby (このイメージの場合は 3.4.8) がプリインストールされた状態になっています。

なぜ標準の rvm では Ruby 4.0 が入らないのか?

ここが今回の核心です。Dev Container の Ruby Feature は非常に便利ですが、記事執筆時点(2026年2月4日)では、Feature に含まれる rvm のバージョンが、最新の Ruby 4.0 のインストールに対応していませんでした。

つまり、devcontainer.json の設定で version: "4.0.0" と指定しても、古い rvm が「そのバージョンはサポートされていない」というエラーとなって Docker イメージのビルドが停止してしまうのです。

この問題を解決する方法のひとつが、コンテナイメージのビルド時に rvm 自体を最新版にアップデートするということになります。その具体的な手順を見ていきましょう。

問題の分析:dvc-ruby4-202602 が解決しようとしていること

さて、理論的な背景がわかったところで、今回のプロジェクト dvc-ruby4-202602 が具体的にどのような問題に直面し、何を解決しようとしているのかをもう少し掘り下げてみましょう。

発端:最新 Ruby の採用と壁

このプロジェクトの発端はシンプルでした。「hiro345g/dvc:ruby-202602 という既存の Dev Container 環境を使って、リリースされたばかりの Ruby 4.0 を試したい!」というものです。

しかし、前章で解説した通り、ベースイメージに含まれる rvm のバージョンが古く、rvm install 4.0.0 コマンドは失敗に終わりました。これが、筆者が直面した最初の「壁」です。

なぜ複数バージョンを共存させるのか?

ここで一つの疑問が浮かびます。「開発コンテナで Ruby 4.0 だけを使いたいなら、古い 3.4.8 は不要ではないか?」

Ruby 4.0 環境専用の開発コンテナなら、確かにその通りです。しかし、実世界の多くのプロジェクトでは、既存のアプリケーションの安定動作を保証しつつ、新しいバージョンでのテストや先行開発を進めたいという要求があります。

Dev Container 用の Ruby Feature が rvm を使って Ruby の複数のバージョンを管理できるように設計されているのは、開発コンテナ用 Docker イメージのビルド時に指定されたバージョンの Ruby をインストールしやすくするためだけでなく、開発時にRubyの複数のバージョンを使いたい場面があることを見越しているためだと、筆者は推察しています。

ということで、dvc-ruby4-202602 プロジェクトでも Ruby の複数のバージョンを管理できることが要件に含まれます。

想定しているのは、このようなケースです。まず、hiro345g/dvc:ruby-202602 環境で、Ruby 3.4.8 を使って実装されたアプリケーションがあるとします。これが稼働可能な環境を維持しつつ、Ruby 4.0.0 対応を同じ開発コンテナで作業できるように、dvc-ruby4-202602 を用意します。それから、使用する開発コンテナ用の Docker イメージを hiro345g/dvc:ruby-202602 から dvc-ruby4-202602 へ置き換えます。

そのため、このプロジェクトは、単に「Ruby 4.0 をインストールする」とか「Ruby 4.0 用 Docker イメージを用意する」といったものでは済みません。同じ開発コンテナで、必要に応じて二つのバージョンを自由に切り替えられるということに対応する必要があります。

Docker イメージが消費するディスク容量のサイズも抑制するために、次の方針での対応とすることにしました。

  1. hiro345g/dvc:ruby-202602 をベースとして rvm 自体を最新版に更新
  2. 最新の rvm を使って、Ruby 3.4.8 と Ruby 4.0.0 の両方をインストール

これにより、古いコードのメンテナンスと、未来に向けた新しいコードの開発を、同じ環境でシームレスに行えるようになります。

手元で Ruby Feature を利用した開発コンテナ用 Docker イメージを用意している場合は、今回紹介する方法で Ruby 4.0.0 に対応する新しい開発コンテナ用 Docker イメージを自作できるようになります。

dvc-ruby4-202602 のカスタマイズ詳細

それでは、カスタマイズの詳細を見ていきましょう。ここでは、各設定ファイルがどのように連携し、Ruby 4.0 を含む複数のバージョンが動作する環境を構築しているのかを解説します。

プロジェクトの全体像

まず、プロジェクトのファイル構成を見てみましょう。

dvc-ruby4-202602/
├── README.md
├── build-image/  # 開発コンテナ用 Docker イメージのビルド用
│   ├── Dockerfile
│   ├── build.sh
│   ├── compose.yaml
│   └── devcontainer.json
│---------- これ以降のものは、開発コンテナ用
├── compose.yaml
├── .devcontainer/
│   └── devcontainer.json
└── workspace_share/
    └── rubyapp001/
        ├── Gemfile
        ├── Gemfile.lock
        ├── README.md
        └── main.rb

重要なのは以下の4つのファイル/フォルダです。

ファイル/フォルダ 説明
build-image/Dockerfile カスタム Docker イメージの設計図
compose.yaml 開発コンテナ起動時に利用するリソース定義
.devcontainer/devcontainer.json 開発コンテナの設定ファイル
workspace_share/ 開発コンテナと Docker ホストのファイル共有用フォルダ

build-image ディレクトリにも compose.yamldevcontainer.json がありますが、これはイメージ作成用で、実際に Dev Container(開発コンテナ)を起動するのはプロジェクトルートにある設定ファイル群です。

build-image/Dockerfile: rvm をハックする

このカスタマイズの核心である Dockerfile を見ていきましょう。ベースイメージ hiro345g/dvc:ruby-202602 に対して、いくつかの重要な処理を追加しています。

FROM hiro345g/dvc:ruby-202602

# rvm を最新版へ変更、インストールされている 3.4.8 は使えなくなるのでアンインストール
RUN export GNUPGHOME="/tmp/tmp-gnupg" \
    && mkdir -p "$GNUPGHOME" \
    && chmod 700 "$GNUPGHOME" \
    && curl -sSL https://rvm.io/mpapis.asc | gpg --batch --import - \
    && curl -sSL https://rvm.io/pkuczynski.asc | gpg --batch --import - \
    && /usr/local/rvm/bin/rvm remove 3.4.8 || true \
    && /usr/local/rvm/bin/rvm get head \
    && /usr/local/rvm/bin/rvm reload \
    && /usr/local/rvm/bin/rvm all do rvm repair all \
    && rm -rf "$GNUPGHOME"

# rvm を最新版に変更してから、3.4.8、4.0.0 の順にインストール後、デフォルトを 4.0.0 に設定
RUN /usr/local/rvm/bin/rvm install 3.4.8 \
    && /usr/local/rvm/bin/rvm install 4.0.0 \
    && /usr/local/rvm/bin/rvm alias create default 4.0.0

# gem のシステムアップデートと bundler のアップデート
RUN /usr/local/rvm/bin/rvm all do gem update --system \
    && /usr/local/rvm/bin/rvm all do gem install bundler \
    && chown -R node:rvm /usr/local/rvm

ポイントを分解して見ていきましょう。

rvm のアップグレード

まず、ベースイメージにプリインストールされている Ruby 3.4.8 を削除します。最新版の rvm にすると、既存のインストールされている Ruby 環境が壊れるからです。後で最新版の rvm で再インストールします。

rvm remove 3.4.8

次が最も重要なコマンドです。head を指定することで、rvm を最新の開発版にアップグレードします。これにより、まだ公式リリースには含まれていない Ruby 4.0 のような最新バージョンをインストールできるようになります。

rvm get head

このコマンドを実行するときに、GPG 関連のファイルが必要となるので、gpg コマンドを事前に実行しています。Ruby Feature の install.sh と同様に、この情報は環境変数 GNUPGHOME で指定した一時的なパスに保存して、不要になった時点で削除しています。これにより、Docker イメージに不要なファイルが残らないようにしています。

なお、rvm get はリモートリポジトリのコードを現在のインストールに反映するため、既存の rvm の状態を更新する必要がでてきます。また、rvm の内部で破壊的な変更があると動かなくなるので修復も必要な場合があります。そのため rvm コマンドの reloadrepair all を実行します。

rvm reload
rvm repair all

複数バージョンの Ruby をインストール

最新になった rvm を使い、改めて Ruby 3.4.8 をインストールします。これにより、既存の Ruby 開発環境との互換性を維持します。

rvm install 3.4.8

そして、本命の Ruby 4.0.0 をインストールします。

rvm install 4.0.0

コンテナにログインした際に、デフォルトで使われる Ruby のバージョンを 4.0.0 に設定します。

rvm alias create default 4.0.0

Gem 環境の整備

ここでは、全ての Ruby バージョン(3.4.8 と 4.0.0)に対して gem 自体のアップデートと、bundler のアップデートをしています。bundler は再インストールで新しいバージョンのものにしています。

これは、実際に使ってみたところ、初期インストールされている gembundler が古いことからエラーが発生したので対応しました。ただ、3.4.8 のものについては、既存の環境がある場合は、アップデートしない方が良い場合もあるでしょう。自分の環境に合わせて調整すると良いでしょう。

rvm all do ... は、インストールされている全ての Ruby バージョンに対して、同じコマンドを実行するための便利な rvm の機能です。

compose.yaml: カスタムイメージをコンテナとして定義

次に、compose.yaml が、自作のカスタムイメージを使って、どのようなコンテナを起動するか見てみましょう。

name: dvc-ruby4
services:
  dvc-ruby4:
    image: dvc-ruby4-202602
    container_name: dvc-ruby4
    # ... (中略) ...
    volumes:
      - workspace-data:/home/node/workspace
      - rvm-data:/usr/local/rvm
      - type: bind
        source: ${SHARE_DIR:-./workspace_share}
        target: /share
    # ... (中略) ...

image には、dvc-ruby4-202602 を指定します。この Docker イメージは build-image/Dockerfile で定義した自作のものです。そのため、事前に build-image/build.sh を実行して、Docker イメージをビルドしておく必要があります。

volumes では、rvm-data ボリュームと ./workspace_share フォルダのマウントをしています。

rvm-data/usr/local/rvm フォルダにマウントしています。これにより、rvm でインストールした Ruby や Gem がコンテナを再作成しても消えずに保持され、2回目以降の起動が高速になります。

Docker ホストの ./workspace_share フォルダは、コンテナ内の /share にバインドマウントしています。これにより、Docker ホストのファイルを開発コンテナにアタッチした VS Code の画面内にあるエクスプローラで参照したりコピーしたりできるようになります。また、開発コンテナ内で /share 内にあるファイルやフォルダを編集すると、Docker ホストへ即座に反映されます。

.devcontainer/devcontainer.json: VS Code との連携

最後に、devcontainer.json で開発コンテナの設定をしています。

{
  "name": "dvc-ruby4",
  "dockerComposeFile": ["../compose.yaml"],
  "service": "dvc-ruby4",
  "workspaceFolder": "/home/node/workspace",
  "customizations": {
    "VS Code": {
      "extensions": [
        "shopify.ruby-lsp",
        // ... (他の拡張機能) ...
      ]
    }
  }
}

dockerComposeFile には、使用する Docker Compose ファイルを指定します。複数指定可能です。ここでは ["../compose.yaml"] としてあります。

service には、使用する compose.yaml 内のどのサービスを開発コンテナとして利用するかを指定します。今回使用する compose.yaml では、dvc-ruby4 しかサービスがないので、それを指定してあります。もし複数のサービスがあっても、この service で使用するものを指定できるようになっています。

なお、これについては、開発コンテナ用の Docker イメージを使うサービスである必要があるので、もし dvc-ruby4image に開発コンテナ化に対応しないものが指定された場合は実行時にエラーとなります。

workspaceFolder には、コンテナに接続した際に、エディタで開くデフォルトのフォルダを指定します。ここでは /home/node/workspace としてあります。開発コンテナでは、Docker ホストのファイルシステムをバインドして使うメリットはあまりないので、Docker ボリュームにマウントされたフォルダを指定し、リポジトリは Docker ボリュームに保持して開発作業をするという想定をしています。

customizations.VS Code.extensions には、コンテナ内で自動的にインストール・有効化される VS Code 拡張機能のリストです。ruby-lsp など、Ruby 開発を快適にするためのツールが含まれています。

これら3つのファイルが連携することで、「Dockerfile で定義された特殊な環境を、compose.yaml がコンテナとして起動し、devcontainer.json が VS Code と結びつける」という一連の流れが完成します。

ハンズオン:構築した環境を使ってみよう

理論がわかったところで、いよいよ実際にこの環境を動かしてみましょう。ここでは、プロジェクト用フォルダを用意してから、開発コンテナを起動し、Ruby のバージョンを切り替えるまでの一連の流れを体験します。

前提条件

  • Git
  • Docker Desktop
  • Visual Studio Code と Dev Containers 拡張機能
    • または、Dev Container に対応した他のエディタ(Zed など)

リポジトリのクローンとイメージのビルド

設定済みの開発コンテナプロジェクト dvc-ruby4-202602 を用意しました。次の GitHub リポジトリで公開しています。

ローカルで使えるようにするには次のようにします。

curl -L -o dcfwd-main.zip https://github.com/hiro345g/dcfwd/archive/refs/heads/main.zip
unzip dcfwd-main.zip
mv dcfwd-main/2026/dvc-ruby4-202602 .
rm -rf dcfwd-main dcfwd-main.zip

次に、Dockerfile で定義したカスタムイメージをビルドします。このプロジェクトには、ビルドを簡単に行うためのシェルスクリプト build.sh が用意されています。

先に使用する Docker イメージをプルしておくと良いでしょう。少し時間がかかるので、気長に待ちましょう。build.sh のスクリプトを実行すると自動でプルするので作業を分けたくない場合はスキップして良いです。

docker image pull hiro345g/dvc:ruby-202602

それから、次のようにスクリプトを実行します。

bash ./build-image/build.sh

このコマンドは、内部で @devcontainers/cli を呼び出し、devcontainer.json の設定に従って dvc-ruby4-202602 という名前の Docker イメージをビルドします。こちらもビルドに少し時間がかかります。あらかじめ hiro345g/dvc:ruby-202602 のイメージをプルしていない場合はかなり時間がかかるので、気長に待ちましょう。

Dev Container の起動

イメージのビルドが完了したら、VS Code で dvc-ruby4-202602 プロジェクトのルートディレクトリを開きます。

VS Code が .devcontainer ディレクトリを検知し、右下に「Reopen in Container」という通知が表示されるはずです。

このボタンをクリックすると、VS Code が devcontainer.jsoncompose.yaml の設定に従って開発コンテナを起動し、自動的に開発コンテナ内の環境にアタッチします。

動作確認

開発コンテナにアタッチできたら、ターミナルを開いて(Ctrl+@ または Ctrl+J)、本当に環境が構築されているか確認してみましょう。

デフォルトバージョンの確認

まず、デフォルトの Ruby のバージョンを確認します。Dockerfile4.0.0 をデフォルトに設定したので、その通りになっているはずです。

$ ruby -v
ruby 4.0.0... # (バージョン情報)

インストールされている Ruby のリスト表示

次に、rvm が管理している Ruby のバージョン一覧を見てみましょう。

$ rvm list

rvm rubies

   ruby-3.4.8 [ x86_64 ]
=* ruby-4.0.0 [ x86_64 ]

# => - current
# =* - current && default
#  * - default

3.4.84.0.0 の両方がインストールされており、4.0.0 が現在選択されていて、かつデフォルト(=*)になっていることがわかります。

rvm use でバージョンの切り替え

rvm use コマンドで、Ruby のバージョンを 3.4.8 に切り替えてみましょう。

$ rvm use 3.4.8
Using /usr/local/rvm/gems/ruby-3.4.8

$ ruby -v
ruby 3.4.8... # (バージョン情報)

サンプルアプリケーションの実行

最後に、workspace_share/rubyapp001 にある簡単なサンプルアプリケーションを実行してみましょう。
その前に、バージョン切り替えの効果が分かりやすいように、main.rb を少しだけ変更します。
workspace_share/rubyapp001/main.rb を開き、内容を以下のように書き換えてください。

main.rb
puts "Hello, Ruby! from #{RUBY_VERSION}"

RUBY_VERSION は、現在実行している Ruby のバージョンを保持する定数です。
では、準備ができたので、各バージョンで実行してみましょう。

# まずは 4.0.0 に戻しておく
$ rvm use 4.0.0
Using /usr/local/rvm/gems/ruby-4.0.0

$ cd /share/rubyapp001/
$ ruby main.rb
Hello, Ruby! from 4.0.0

# 3.4.8 でも試してみる
$ rvm use 3.4.8
Using /usr/local/rvm/gems/ruby-3.4.8

$ ruby main.rb
Hello, Ruby! from 3.4.8

このように、コンテナを再起動することなく、コマンド一つで Ruby の実行環境を切り替えながら、アプリケーションの動作テストが可能です。

Bundler の利用

Ruby のプロジェクトでは Bundler も一緒に使うことが多いでしょう。

bundle config set --local path 'vendor/bundle'
bundle install
bundle exec ruby main.rb

実行例は次のようになります。

node ~/workspace/rubyapp001 $ ruby --version
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [x86_64-linux]
node ~/workspace/rubyapp001 $ bundle install
Bundle complete! 1 Gemfile dependency, 0 gems now installed.
Bundled gems are installed into `./vendor/bundle`
node ~/workspace/rubyapp001 $ tree ./vendor/
./vendor/
└── bundle
    └── ruby
        └── 4.0.0

4 directories, 0 files
node ~/workspace/rubyapp001 $ bundle exec ruby main.rb
Hello, Ruby! from 4.0.0
node ~/workspace/rubyapp001 $ bundle clean

補足:rvm の代替案としての mise 活用

これまで rvm をカスタマイズする方法を見てきましたが、プロジェクトによっては rvm を使わない選択をしたい場合もあるでしょう。特に、Ruby 以外の言語(Node.js, Python など)も同時に扱うプロジェクトでは、より汎用的なバージョン管理ツールが好まれます。

そこで筆者が代替案として推奨するのは、前に紹介した mise-en-place です。これは、mise.toml ファイル一つで、さまざまな言語のバージョンを統一的に管理できる非常に強力なツールです。また、記事執筆時点で、rvm がサポートしていなかった Ruby 4.0.0 を mise コマンドはサポートしていました。mise であれば、すぐに最新版を利用できるようになることが期待できそうです。

ここでは、rvm の代わりに mise-en-place の mise コマンドを使って、Ruby 3.4.8 と 4.0.0 を管理する方法を補足として紹介します。

hiro345g/dvc:ruby-202602 の mise

hiro345g/dvc:ruby-202602 は最初から mise コマンドが使えます。

この Docker イメージは、hiro345g/dvc:novnc-202602 へ mise-en-place をインストールした Docker イメージをベースとしているからです。

なお、ビルド方法は次の URL で公開しています。

script/install-mise.sh にインストールスクリプトがあり、これを使って mise コマンドをインストールしています。これは公式サイトのドキュメント https://mise.jdx.dev/getting-started.html を参考にして作成したものです。

mise.toml ファイルの作成

mise コマンドが使える環境では、rubyapp001 プロジェクトのルート(dvc-ruby4-202602 コンテナなら、/home/node/workspace/rubyapp001 の直下)に、使用したい Ruby のバージョンを定義した mise.toml ファイルを作成してから、mise install を実行することで、指定したバージョンの ruby コマンドが使えるようになります。

mise.toml
[tools]
ruby = ["4.0.0", "3.4.8"]

この例では ruby の複数のバージョンを使えるようにしています。配列で指定されたバージョンの先頭の要素のものが、このプロジェクトのデフォルトのバージョンとなります。

mise.toml を用意したらインストールします。このとき、フォルダにある mise.toml を信頼する必要があるので、mise trust を実行します。mise は自動で処理される機能があるため、勝手に動作しないように、こういった承認プロセスがあります。それから、mise install を実行します。-y は非対話モードでコマンドを実行するオプションです。

mise trust
mise install -y

この環境では、miseruby をインストールすると、そちらが優先されます。which コマンドで確認できます。

node ~/workspace $ which ruby
/home/node/.local/share/mise/installs/ruby/4.0.0/bin/ruby

このように、mise のものが使われることがわかります。

なお、miservm の両方で ruby をインストールすると混乱するので、もし mise の方で ruby のバージョン管理をするなら、rvm でインストールした ruby はアンインストールしておくと良いでしょう。また、rvm の方で ruby のバージョン管理をするなら mise は無効化しておくと良いでしょう。mise の無効化は ~/.bashrceval "$(~/.local/bin/mise activate bash --shims)"eval "$(~/.local/bin/mise activate bash)" の行を含む if をコメントにすれば良いです。(これは、devcontainer.jsonpostCreateCommand で置換するなどして自動化対応できます)

mise を使ったバージョン管理

misemise.toml のリストの最初のバージョンをデフォルトとして認識します。

デフォルトバージョンを確認するため、インストール後に ruby -v コマンドを実行してバージョンを確認しましょう。

node ~/workspace $ ruby -v
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [x86_64-linux]

ここでは 4.0.0 となっているはずです。

mise use でバージョンの切り替え

mise use コマンドを使うことで、そのシェルセッションで使うバージョンを一時的に切り替えられます。

mise use ruby@3.4.8
ruby -v
mise use ruby@4.0.0
ruby -v

実行例は次のようになります。

node ~/workspace $ mise use ruby@3.4.8
ruby@3.4.8
mise ~/workspace/mise.toml tools: ruby@3.4.8
node ~/workspace $ ruby -v
ruby 3.4.8 (2025-12-17 revision 995b59f666) +PRISM [x86_64-linux]
node ~/workspace $ mise use ruby@4.0.0
ruby@4.0.0
mise ~/workspace/mise.toml tools: ruby@4.0.0
node ~/workspace $ ruby -v
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [x86_64-linux]

このように、mise を使うことで、rvm とは異なるアプローチで、かつ複数の言語にまたがってバージョン管理を統一することができます。開発コンテナ環境をよりモダンに、そして汎用的に保ちたい場合に、非常に有効な選択肢と言えるでしょう。

まとめ:Dev Container と rvm の柔軟な活用

本記事では、dvc-ruby4-202602 プロジェクトを題材に、標準の Dev Container Ruby Feature ではインストールできない最新の Ruby 4.0 を、Dockerfile をカスタマイズすることで導入する方法を解説しました。

重要なポイントは以下の通りです。

  • rvm get head で最新化
  • Dockerfile のカスタマイズ
  • 複数バージョンの共存

Dev Container Feature に含まれる rvm のバージョンが古い場合、rvm 自体を最新版に更新することで、新しいバージョンの Ruby に対応できます。更新の手順について、注意事項がいくつかありますが、Dockerfile に記載して手順をコード化しておけば次からは簡単に対応できるようになります。

また、今回のように devcontainer.jsonfeatures だけに頼るのではなく、Dockerfile を直接編集することで、より柔軟で強力な環境構築が可能になります。features は Ruby 用のものだけでなく、他のものも含めて環境構築を簡単にしてくれるものなので、それを活用した上で、「rvm の更新と Ruby 4.0.0 のインストール」という部分だけカスタマイズしているのが重要です。

mise の紹介もしましたが、Ruby Feature で採用されている rvm は人気があってよく使われているので、これを使って Ruby のバージョン管理をしているプロジェクトは多いはずです。このツールを切り替えるコストが無駄な場合は、そのまま使う選択となるので、これを活用して、一つのコンテナ内で複数のバージョンを管理できるようにするのは、意味があります。もともと rvm を採用しているプロジェクトでは、互換性維持と先行開発を両立できる利点を得るために使っているはずなので、それを継続できるというのが重要です。

Dev Container は非常に強力なツールですが、時として標準機能だけでは解決できない壁にぶつかることがあります。しかし、その内部構造(今回で言えば、Ruby Feature が rvm を使っていること)を理解し、Dockerfile のような低レイヤーのカスタマイズに踏み込むことで、その壁を乗り越えることができます。

この「標準機能でうまくいかなければ、その仕組みを理解して直接カスタマイズする」というアプローチは、Ruby や Dev Container に限らず、あらゆる技術的な問題解決に応用できる普遍的な考え方です。

ぜひ、皆さんのプロジェクトでもこのテクニックを応用して、快適な開発環境を構築してみてください。

Appendix

開発コンテナのビルド時に fetures/ruby で "version":"4.0.0" を指定したときに発生するエラーについても紹介しておきます。

{
  "name": "dvc-ruby",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:24-trixie",
  "features": {
    "ghcr.io/devcontainers/features/ruby:1.3.2": {
      "version":"4.0.0"
    }
  }
}

この devcontainer.json について、@devcontainers/cli でビルドすると、次のようなエラーになります。

$ npm exec --package=@devcontainers/cli -- devcontainer build \
        --workspace-folder .\
        --config ./devcontainer.json \
        --image-name dvc-ruty4-202602
(略)
ERROR: Feature "Ruby (via rvm)" (ghcr.io/devcontainers/features/ruby) failed to install! Look at the documentation at https://github.com/devcontainers/features/tree/main/src/ruby for help troubleshooting this error.
------
Dockerfile-with-features:29
--------------------
  28 |     ENV PATH="/usr/local/rvm/gems/default/bin:/usr/local/rvm/gems/default@global/bin:/usr/local/rvm/rubies/default/bin:/usr/local/share/rbenv/bin:${PATH}"
  29 | >>> RUN --mount=type=bind,from=dev_containers_feature_content_source,source=ruby_0,target=/tmp/build-features-src/ruby_0 \
  30 | >>>     cp -ar /tmp/build-features-src/ruby_0 /tmp/dev-container-features \
  31 | >>>  && chmod -R 0755 /tmp/dev-container-features/ruby_0 \
  32 | >>>  && cd /tmp/dev-container-features/ruby_0 \
  33 | >>>  && chmod +x ./devcontainer-features-install.sh \
  34 | >>>  && ./devcontainer-features-install.sh \
  35 | >>>  && rm -rf /tmp/dev-container-features/ruby_0
  36 |     
--------------------
ERROR: failed to build: failed to solve: process "/bin/sh -c cp -ar /tmp/build-features-src/ruby_0 /tmp/dev-container-features  && chmod -R 0755 /tmp/dev-container-features/ruby_0  && cd /tmp/dev-container-features/ruby_0  && chmod +x ./devcontainer-features-install.sh  && ./devcontainer-features-install.sh  && rm -rf /tmp/dev-container-features/ruby_0" did not complete successfully: exit code: 1

View build details: docker-desktop://dashboard/build/default/default/z07zadbra96mbkjl04u6h0msv
Error: Command failed: docker buildx build --load --build-arg BUILDKIT_INLINE_CACHE=1 -f /tmp/devcontainercli-workspace/container-features/0.82.0-1770507732241/Dockerfile-with-features -t dvc:ruby4-202602 --target dev_containers_target_stage --build-context dev_containers_feature_content_source=/tmp/devcontainercli-workspace/container-features/0.82.0-1770507732241 --build-arg _DEV_CONTAINERS_BASE_IMAGE=dev_container_auto_added_stage_label --build-arg _DEV_CONTAINERS_IMAGE_USER=root --build-arg _DEV_CONTAINERS_FEATURE_CONTENT_SOURCE=dev_container_feature_content_temp /workspace/dvc-ruby4
    at v9 (/workspace/node_modules/@devcontainers/cli/dist/spec-node/devContainersSpecCLI.js:468:1988)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async bD (/workspace/node_modules/@devcontainers/cli/dist/spec-node/devContainersSpecCLI.js:467:1910)
    at async J5 (/workspace/node_modules/@devcontainers/cli/dist/spec-node/devContainersSpecCLI.js:668:2276)
    at async x5 (/workspace/node_modules/@devcontainers/cli/dist/spec-node/devContainersSpecCLI.js:667:5034)
    at async /workspace/node_modules/@devcontainers/cli/dist/spec-node/devContainersSpecCLI.js:485:1188

Discussion