🦉

Neovimをコンテナ化して配布する

に公開

本記事は Vim Advent Calendar 2025 6日目の記事です。表題の通り、Neovimをコンテナ化して利用しているのでやり方を紹介します。

https://adventar.org/calendars/11912

dotfilesの構成

私のdotfilesは3つのモジュールで構成されています。MacOSの設定ファイル (asagi)、NixOSの設定ファイル (azuma)、Neovimのコンテナ (akatsuki) です。

shunsock/dotfiles

このうち、OSの設定ファイル達は次のハードウェアで起動しています。

開発環境

そして、Neovimは全てのハードウェアで起動しています。Nix経由で設定することも可能でしたが、dockerがあればどこでも動くという点が魅力に感じたので使っています。

Neovimをコンテナ化

コンテナの作り方自体は普通のウェブサーバーやCLIと同じです。次の設定はdefaultで起動するNeovimコンテナのDockerfileです。

# ======================================
# Builder Stage
# ======================================
FROM ubuntu:24.04 AS builder

# 引数の定義
ARG ARCH=arm64
ARG NEOVIM_VERSION=v0.11.1
ARG NODE_VERSION=24.x

# 環境変数の設定
ENV DEBIAN_FRONTEND=noninteractive
ENV DOTNET_VERSION=8.0

# 基本パッケージのインストール
RUN apt-get update && apt-get install -y \
    build-essential (その他必要なパッケージ) \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Neovimのインストール
RUN curl -LO "https://github.com/neovim/neovim/releases/download/${NEOVIM_VERSION}/nvim-linux-${ARCH}.tar.gz" \
    && tar xzvf "nvim-linux-${ARCH}.tar.gz" \
    && cp "nvim-linux-${ARCH}/bin/nvim" /usr/local/bin/ \
    && cp -r "nvim-linux-${ARCH}/share/nvim" /usr/local/share/ \
    && rm -rf "nvim-linux-${ARCH}" "nvim-linux-${ARCH}.tar.gz"

# Node.jsのインストール
RUN curl -fsSL "https://deb.nodesource.com/setup_${NODE_VERSION}" | bash - \
    && apt-get update \
    && apt-get install -y nodejs \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# .NET SDKのインストール(Marksman用)
RUN wget "https://packages.microsoft.com/config/ubuntu/24.04/packages-microsoft-prod.deb" -O packages-microsoft-prod.deb \
    && dpkg -i packages-microsoft-prod.deb \
    && rm packages-microsoft-prod.deb \
    && apt-get update \
    && apt-get install -y "dotnet-runtime-${DOTNET_VERSION}"\
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# ======================================
# Runtime Stage
# ======================================
FROM ubuntu:24.04

# 環境変数の設定
ENV DEBIAN_FRONTEND=noninteractive

# 必要なパッケージのインストール
RUN apt-get update && apt-get install -y \
    apt-transport-https (その他必要なパッケージ) \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# 必要なディレクトリの作成とNvim設定のコピー
RUN mkdir -p /root/.config/nvim /root/.local/share/nvim/mason
COPY config/ /root/.config/nvim/

# builderステージからバイナリと必要なツールをコピー
COPY --from=builder /usr/local/bin/nvim /usr/local/bin/
COPY --from=builder /usr/local/share/nvim /usr/local/share/nvim
COPY --from=builder /usr/bin/node /usr/bin/
COPY --from=builder /usr/bin/npm /usr/bin/
COPY --from=builder /usr/bin/dotnet /usr/bin/

ENTRYPOINT ["/usr/local/bin/nvim"]

補足:

デプロイ

コンテナができたらデプロイします。どこでも良いですが、私はDockerHubを利用しています。

ちなみにtagが、defaultとpythonで分かれているのは、軽量化のためです。色々な言語の言語サーバーを入れると簡単に1GB越えます。今は、defaultが200MBぐらいです。

自分はデプロイコマンドを覚えられないのでTaskfileを使ってデプロイしています。

tasks:
  push:default:arm:
    desc: "build and push akatsuki-default"
    dir: akatsuki-default-arm
    cmds:
      - docker build -t akatsuki-default-arm .
      - docker tag akatsuki-default-arm tsuchiya55docker/akatsuki:default-arm-{{.VERSION}}
      - docker push "tsuchiya55docker/akatsuki:default-arm-{{.VERSION}}"
    silent: true

ハードウェアから呼び出す

高速化のためにキャッシュをホストOS側に置いている関係上、起動コマンドが長いです。当然覚えられないので、シェルのaliasを貼っています。(この設定は shunsock/dotfiles/(asagi|azuma)/configs/zsh/ に書いてあります)

alias v='docker run -it --rm \
  -v "$PWD":/workspace \
  -v "$HOME/.akatsuki-default/share":/root/.local/share/nvim \
  -v "$HOME/.akatsuki-default/cache":/root/.cache/nvim \
  -v "$HOME/.akatsuki-default/state":/root/.local/state/nvim \
  -w /workspace \
  tsuchiya55docker/akatsuki:default-arm-0.0.2'

alias vpy='docker run -it --rm \
  -v "$PWD":/workspace \
  -v "$HOME/.akatsuki-python/share":/root/.local/share/nvim \
  -v "$HOME/.akatsuki-python/cache":/root/.cache/nvim \
  -v "$HOME/.akatsuki-python/state":/root/.local/state/nvim \
  -w /workspace \
  tsuchiya55docker/akatsuki:python-0.0.2'

このようにaliasを貼っておけば煩わしさもありません。

$ docker login
$ v # vpy

ホストOSとのコピー & ヤンクをシームレスにする

ここまで来て起動すると分かると思うのですが、Dockerコンテナ内のNeovimがヤンクしてもクリップボードに反映されません。

そこでOSC 52 (Operating System Command 52)を有効化します。これを有効化すると、ホスト側のOSのクリップボードにDockerコンテナ内のyankした情報が保持できます。WezTermの場合次のコードを設定ファイルに記述すると有効化できます。

-- OSC52を有効化する
config.enable_csi_u_key_encoding = true

やってみた感想

楽です。とりあえずDockerさえあれば動くという安心感は私の心に平穏を与えてくれることが分かりました。

これからやること

継続的インテグレーション・デプロイ

Taskfileで管理しているためそこまで負担にはなっていないですが、これから管理するものが増えるのでGitHub Actionsに載せようと考えています。

デプロイもそうですし、DockerのLinterとかも入れる予定です。

当日追記:

設定の共通化

共通の設定が多いのでビルドプロセス時に、共通と固有設定をマージするような仕組みを作ろうと考えています。また、Dockerfileも共通部分が多いので、そのうちマージしたいですね。

言語を増やす

RustとGo, C#を書くのでコンテナの拡充をしたいです。

Discussion