Claude Media
Claude CodeをProxmoxのLXCコンテナで動かす手順

Claude CodeをProxmoxのLXCコンテナで動かす手順

ProxmoxのLXCコンテナでClaude Codeを動かす手順です。テンプレート選びとヘッドレス環境でのログイン完了までを扱います。

Proxmox LXCでClaude Codeを動かすとは

Proxmox VEのLXCは、ホストのカーネルを共有するOSレベルのコンテナです。仮想マシンのようにCPUやデバイスをエミュレートしないため、起動が速くメモリの無駄も少なくなります。Claude Codeを動かすだけの軽量な作業環境が欲しいとき、VMより手早く用意できる選択肢です。

コンテナ内で動く実体はLXCでもDockerでも同じ「Claude Codeのネイティブバイナリ」です。違いはホストとの分離の仕方だけなので、インストール自体はLinux版のインストーラーがそのまま使えます。

ただしLXCはDockerとは別物です。Debian・Ubuntu・Alpineなどフルディストリビューションのテンプレートからコンテナを作る点が、アプリケーション単位のイメージを積むDockerと違います。この違いが、後述するインストール手順の分岐に直結します。

前提条件 — コンテナ種別とリソースを決める

Claude Codeのシステム要件は次の通りです。

  • ハードウェア: 4GB以上のRAM、x64またはARM64
  • 対応OS: Ubuntu 20.04以降、Debian 10以降、Alpine Linux 3.19以降
  • ネットワーク: インストーラーと自動更新が到達できるインターネット接続

Proxmox VEのテンプレート一覧にはDebian・Ubuntu・Alpineがすべて含まれるので、対応OSの条件はテンプレート選びの時点で満たせます。残る判断はコンテナの権限モードです。

Proxmox VEはコンテナ作成時に「unprivileged(非特権)」と「privileged(特権)」を選べます。unprivilegedがデフォルトで、コンテナ内のroot UIDはホスト側の一般ユーザーにマッピングされます。コンテナ内で権限を奪われても、影響はホストの一般ユーザー相当に留まる設計です。Claude Codeを動かすだけであれば、unprivilegedのままで支障はないと考えられます。特権が要るのはDockerを入れ子にする場合など限られた場面だけです(詳細は後述のつまずき節)。

公式のシステム要件は4GB以上のRAMです。これは実行時の要件で、インストール自体は約512MBの空きメモリがあれば通ります(根拠は後段の「よくあるつまずき」)。CT作成時は要件どおり4GB以上を割り当てるのが安全です。4GBを下回る値で作る場合は、インストール後にメモリ不足でKilledが出ないか確認してください。

手順1 — LXCコンテナを作成する

まずテンプレートを取得します。Proxmox VEのGUIからでもコマンドラインからでも可能です。

# テンプレート一覧を更新して確認
pveam update
pveam available --section system
 
# 使いたいテンプレートをローカルストレージへダウンロード
pveam download local debian-12-standard_12.7-1_amd64.tar.zst

テンプレートを取得したら、pct create でコンテナを作ります。

pct create 200 local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
  --hostname claude-code \
  --cores 2 \
  --memory 4096 \
  --swap 512 \
  --net0 name=eth0,bridge=vmbr0,ip=dhcp \
  --rootfs local-lvm:8 \
  --unprivileged 1

CTID(この例では200)はクラスタ内で一意な番号です。--rootfsのディスクサイズは8GBを例にしています。Claude Codeのバイナリ自体は小さいので、この程度でも余裕があります。作成後はpct start 200で起動し、pct enter 200でコンテナのシェルに入れます。

pct createが生成するコンテナ設定ファイルの例

pct createの実行結果は/etc/pve/lxc/<CTID>.confに次のような形式で保存されます。

ostype: debian
arch: amd64
hostname: claude-code
memory: 4096
swap: 512
net0: name=eth0,bridge=vmbr0,ip=dhcp
rootfs: local-lvm:vm-200-disk-0,size=8G
unprivileged: 1

このファイルはviやnanoで直接編集もできますが、変更を反映するにはコンテナの再起動が要ります。反映漏れを避けるならpctコマンドかGUI経由で変更するほうが確実です。

手順2 — テンプレート別にClaude Codeをインストールする

pct enterでコンテナのシェルに入ったら、テンプレートの種類によってひと手間の要否が変わります。

テンプレート標準インストールコマンドが通るか追加で必要な作業
Debian 12 / Ubuntu 22.04以降標準インストールコマンドが通るか◎そのまま通る追加で必要な作業なし
Alpine 3.19以降標準インストールコマンドが通るか△失敗する追加で必要な作業bash・curl・libgcc・libstdc++・ripgrepの追加 + USE_BUILTIN_RIPGREP=0

Debian・Ubuntuテンプレートでは、そのままインストールコマンドが動きます。

curl -fsSL https://claude.ai/install.sh | bash

Alpineテンプレートは軽量な分、bashやcurlが最初から入っていません。Alpineや他のmusl/uClibc系ディストリビューションでは、これらが無いとインストールコマンド自体がnot foundエラーで失敗します。先に依存パッケージを入れます。

apk add bash curl libgcc libstdc++ ripgrep

ripgrepはAlpineのcommunityリポジトリに入っています。apkがパッケージを見つけられない場合は、使っているAlpineのバージョンに合わせてリポジトリを追加してください。

echo "https://dl-cdn.alpinelinux.org/alpine/v3.22/community" >> /etc/apk/repositories  # v3.22は例。使用中のAlpineのバージョンに置き換える
apk update
apk add ripgrep

依存を入れたら、settings.jsonにUSE_BUILTIN_RIPGREPを0で設定してから、通常のインストールコマンドを実行します。

{
  "env": {
    "USE_BUILTIN_RIPGREP": "0"
  }
}

この設定を忘れると、インストール自体は通ってもClaude Codeが内蔵のripgrepバイナリを起動できず、検索系の機能でエラーが出ます。

Alpineにはbashとcurlを用意する代わりの経路もあります。Claude Codeはapt・dnf・apkの署名付きリポジトリも配布しており、Alpineが標準で持つwgetだけでセットアップできます。

wget -O /etc/apk/keys/claude-code.rsa.pub \
  https://downloads.claude.ai/keys/claude-code.rsa.pub
echo "https://downloads.claude.ai/claude-code/apk/stable" >> /etc/apk/repositories
apk add claude-code

このリポジトリ経由のインストールはapt・dnf・apkのシステム更新に乗るぶん、Claude Code自身の自動更新では更新されません。バージョンを固定して運用したいLXCには、むしろ都合がよい特性です。どちらの経路でも、libgcc・libstdc++・ripgrepはランタイムで必要なままなので、手順の前半で入れた依存はそのまま活かせます。

同じテンプレートを複数のCTに繰り返し展開する運用なら、プラグインやマーケットプレイスを事前に焼き込んでおく方法がClaude Codeプラグインをコンテナへ事前展開する手順にあります。LXCテンプレートのカスタマイズと組み合わせれば、pct cloneで複製するたびに手作業が減ります。

手順3 — インストールを確認する

インストールが終わったら、まずバージョンを確認します。

claude --version

2.1.211 (Claude Code)のようにバージョン番号が表示されれば成功です。command not foundになる場合は、PATHにインストール先が通っていないことが大半なので、シェルを再起動するか~/.local/binがPATHに含まれているか確認します。もう一段詳しい診断が要るときはclaude doctorを使います。

claude doctor

claude doctorはセッションを開始せず、インストール状態と設定ファイルの妥当性を読み取り専用で診断します。LXCコンテナ特有の項目はありませんが、依存の入れ忘れがあればここでエラーとして表示されることがあります。

手順4 — ヘッドレス環境でログインを完了する

LXCコンテナにはブラウザがありません。claudeを起動してログインを始めると、通常のOAuthフローは自動でブラウザへリダイレクトしますが、コンテナの中では受け側がいないため止まります。

WSL2・SSH経由のリモート・コンテナでは同じ現象が起きます。サインイン後にブラウザへ自動で戻る代わりに、ログインコードが画面に表示されるので、それをPaste code here if promptedのプロンプトへ貼り付ければ完了します。対話プロンプトへの貼り付けがうまく効かない端末では、標準入力からコードを読むclaude auth loginを使う経路に切り替えられます。

claude auth login

表示されたURLは、コンテナの外にある手元のPCやスマホのブラウザで開いて構いません。ログイン後に出るコードをコピーし、コンテナ側のターミナルに貼り付ければ認証が完了します。GitHub Codespacesのような使い捨てのクラウド開発環境でも同じ発想のヘッドレスログイン手順が使われているので、迷ったら手順を見比べると理解が早いです。

よくあるつまずき

インストールがKilledで止まる。Linuxのメモリ不足でOOM Killerがインストールプロセスを終了させたサインです。インストールには約512MBの空きメモリが要ります。LXCなら原因の切り分けは単純で、割り当てたメモリの数値を疑うのが早い方法です。pct set 200 --memory 2048のようにCTのメモリを増やしてpct reboot 200するだけで解消することがほとんどです。スワップを足す対処法もありますが、コンテナへメモリを足す方が確実です。

libstdc++.so.6やlibgcc_s.so.1が見つからないと言われる。glibcベースのシステムなのに、musl向けのバイナリが誤って選ばれたときに出るエラーです。ldd --versionでGNU libcと出ればglibc、muslと出ればAlpineなどのmusl系です。Alpineテンプレートで本当にmuslだった場合は、手順2のapk add libgcc libstdc++ ripgrepで解決します。

rootのまま/直下でインストールすると固まる。Dockerコンテナでの報告例では、rootで/からcurl | bashを実行するとインストーラーがファイルシステム全体を走査してハングします。LXCもデフォルトでrootログインなので、作業ディレクトリを/rootや/tmpに移してから実行しておくと同じ轍を避けやすくなります。

コンテナの中でDockerも動かしたい。unprivilegedコンテナは既定でkeyctl()システムコールを塞いでいて、これを有効にしないとコンテナ内でDockerを動かせません。有効化するにはpct set 200 --features keyctl=1,nesting=1でnestingと合わせて許可します。nestingを有効にするとホストのprocfs・sysfsがコンテナに露出するため、共有ホストで動かす場合は影響範囲を理解した上で使う設定です。MCP経由でコンテナを操作する構成を検討しているなら、Docker MCP ToolkitでClaude Codeからコンテナを操作する設定が具体的な接続手順を扱っています。

ホストのディレクトリをbind mountしたらパーミッションエラーが出る。unprivilegedコンテナはUIDマッピングの都合で、コンテナ内のroot(UID 0)がホスト側の一般ユーザーへ変換されます。そのままホストのディレクトリをbind mountすると、ホスト側のファイル所有者と噛み合わず書き込み権限で詰まることがあります。認証情報をホストから遮断しつつ安全にマウントを設計する考え方はClaude CodeをDevContainerで安全に動かす完全実装でも扱っているので、LXCでの権限設計に流用できます。

まとめ

Debian・Ubuntuテンプレートなら、そのままのインストールコマンドがLXCの中で通ります。Alpineを選ぶ場合だけ、bash・curl・libgcc・libstdc++・ripgrepの追加とUSE_BUILTIN_RIPGREP=0のひと手間が要ります。ログインはブラウザが無い前提で、表示されたコードを手元のブラウザとコンテナ間でやり取りする形になります。動かない場合の大半は、割り当てたメモリ不足かmuslとglibcの取り違えのどちらかです。すでにProxmoxでLXCを運用していて、開発用の使い捨て環境をもう1つ増やしたい人に向いた手順です。

この記事を共有:XはてブLinkedIn