Tomoya前回、ローカルでのシステム動作が完了しました。
前回の記事はこちらから👇

ここからはいよいよ本番であるVPSに環境を構築し、実際にシステムを導入して、VPS上で24時間稼働する音声チャットが成立するのかを検証していきます。
ある程度の想定はしていたものの、「新米VPSユーザーなので何が起きるかわからない」という前回の言葉通り、少し緊張感を持ちながら、AIにサポートしてもらいつつ進めていきます。
VPSにDockerを導入する
まずは、ローカルで行ったのと同じように「Docker」をVPSに導入していきます。
ここで驚いたのが
これまでのapt install一発とは違い、公式リポジトリを登録するところから始まるという点でした。
ローカルではシンプルにコマンドでインストールできましたが、今回はDockerの大元から直接引っ張ってくる形になります。そのため「仕入れ先の追加 → 正規品であることの検証準備 → インストール」というDockerを構築する順番になります。
Ubuntu公式のインストール手順を提示してもらい、以下のコマンドを1つずつ実行していきました。
- sudo apt update
- sudo apt install -y ca-certificates curl gnupg
- sudo install -m 0755 -d /etc/apt/keyrings
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
- sudo chmod a+r /etc/apt/keyrings/docker.gpg
- echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
- sudo apt update
- sudo apt install -y docker-ce docker-ce-cli containerd.io
各コマンドの説明
| コマンド | 意味・役割 |
|---|---|
apt update | 今登録されている仕入れ先の在庫リストを最新化します。インストール前の準備運動です。 |
apt install ca-certificates curl gnupg | 準備作業。「証明書を確認する道具」「ダウンロードする道具」「署名を検証する道具」を用意しています。 |
install -m 0755 -d /etc/apt/keyrings | 次に取得する鍵を置くための保管場所(ディレクトリ)を、適切な権限で作成します。 |
curl ... | gpg --dearmor -o ... | Docker公式の「印鑑(GPGキー)」を取得し、後で照合できる形式にして保存します。これがないと、ダウンロードしたものが本当にDocker公式のものか確認できません。 |
chmod a+r ...docker.gpg | 保存した鍵を、aptが読み取れる状態にしています。 |
echo "deb [...]" | tee /etc/apt/sources.list.d/docker.list | ここが核心です。「Docker公式のリポジトリ(仕入れ先)」を新しく登録しています。先ほどの鍵とセットで登録するため、この仕入れ先から届く品物は署名で検証されます。 |
apt update(2回目) | 新しく登録した仕入れ先も含めて、在庫リストを最新化します。この一手間がないと、次のインストールで「そんなパッケージは無い」と怒られます。 |
apt install -y docker-ce docker-ce-cli containerd.io | ここでようやく、登録した仕入れ先(Docker公式)から、Docker本体をダウンロード・インストールしています。 |
長く見えるのは「安全に正規品を入手するための下準備」が丁寧なだけ
やっていることは鍵を入れる → 仕入れ先を登録する → リストを更新する → 入れるの4ステップです。

docker --versionでバージョンがきちんと表示された時は、正しくインストールされたことがわかり、素直に嬉しかったです。個々のDockerコマンドについても、正直よくわかっていない部分が多かったので、その都度AIに聞くことで、それぞれにどういう意味があって何をしているのかを理解しながら進められたのも良かったです。

管理者権限を省略する
Dockerの操作はもともと管理者権限が必要なため、毎回 sudo を付けなければいけません。そこで、ユーザーを docker グループに所属させて、権限を持った状態で操作できるようにします。
sudo usermod -aG docker tomoya
usermod | 既存ユーザーの設定を変更するコマンド |
|---|---|
-aG docker | docker グループに「追加(append)」する指定。-a を忘れると、今まで所属していた他のグループから外れてしまうため必須です。 |
tomoya | 対象のユーザー名。自分のユーザー名に読み替えてください。 |
ここで重要なのが、「どのグループに所属しているか」はログインした瞬間に読み込まれるという点です。設定を変えても、今つながっているセッションには反映されません。
そのため、一度 exit で切断し、繋ぎ直す必要があります。
exit
# 再接続してから確認
docker --version
sudo なしでバージョンが表示されれば成功です。
docker グループに入れたユーザーは、コンテナ経由でサーバー全体を操作できるため、事実上の管理者権限を持つことになります。
利便性と引き換えのリスクなので、信頼できる自分専用のユーザーにだけ設定してください。
共用サーバーや不特定多数が触る環境では、都度 sudo を使う運用のほうが安全です。
VOICEVOXエンジンを「あえて公開しない」設定で起動する
TomoyaDockerが使えるようになったので、次に【口】であるVOICEVOXエンジンを導入していきます。
コマンド自体は以前ローカルで実行したものとほぼ同じです。
ただし今回は、このエンジンをあえて外部に公開しない設定で起動します。
外部に公開しない理由
Tomoyaこれはセキュリティの観点からの判断です。
これまでの記事では、外部(ブラウザ)からアクセスできるようにするために、ポート開放やファイアウォールの許可を進めてきました。
しかし今回、このVOICEVOXエンジンにアクセスするのは自分ではなく、【頭】であるGeminiを経由した処理だけです。だとすれば、わざわざ外部に公開する必要はありません。
ポート公開の書き方を少し変えるだけで、アクセスできる範囲が大きく変わります。
| 書き方 | 誰がアクセスできるか |
|---|---|
-p 50021:50021(ローカルでの書き方) | すべての経路から受け付けます。VPSでこれを使うと、インターネット上の誰でもこのポートを叩ける状態になります。 |
-p 127.0.0.1:50021:50021(今回の書き方) | VPSの内部(自分自身)からのみ受け付けます。外部から直接この50021番ポートにアクセスすることはできません。 |
この設計の副産物として、ufw やパケットフィルターで50021番を許可する必要すらありません。
ファイアウォールで塞ぐのではなく、そもそも外に出さないという考え方です。
設定漏れによる事故が原理的に起きないぶん、より確実です。
このシステムを不特定多数に共有する予定は今のところありませんが、それでも、無用なセキュリティホールを1つ埋めておけたことには、確かな安心感がありました。
今後、別のプロジェクトで同じように「外部に公開しない」設定にしたい場面が出てくると思うので、これは1つ良い勉強になったポイントです。
①:イメージを取得する
まず、VOICEVOXエンジンのイメージ(設計図)をVPSにダウンロードします。
docker pull voicevox/voicevox_engine:cpu-ubuntu20.04-latest
この時点ではまだ何も動いていません。あくまで「設計図を手元に取り寄せた」段階です。
※タグの cpu- は、GPUを持たないVPS向けのCPU版という意味です。
なお docker run は手元にイメージが無ければ自動で取得してくれるので、この pull は省略もできます。それでも先に実行しておくと、ダウンロードの失敗と起動の失敗を切り分けられるので、数GBの通信が走る初回は分けておくほうが安心です。
②:コンテナを起動する
取得した設計図から、実際に動くコンテナを起動します。
docker run -d --name voicevox --restart unless-stopped -p 127.0.0.1:50021:50021 voicevox/voicevox_engine:cpu-ubuntu20.04-latest
1行が長いので、オプションごとに分解しておきます。
-d | バックグラウンドで動かします。これがないとターミナルが占有され、SSHを切ると止まってしまいます。 |
|---|---|
--name voicevox | コンテナに名前を付けます。以降は長いIDではなく docker logs voicevox のように名前で操作できます。 |
--restart unless-stopped | VPSを再起動しても自動でまた立ち上がる設定。Docker側の「pm2のstartup」に相当する部分です。「自分で明示的に止めたとき以外は復帰する」という意味なので、意図的な停止は尊重されます。 |
-p 127.0.0.1:50021:50021 | 先ほど説明した今回の一番のポイント。ポートの公開範囲をVPS内部だけに限定しています。 |
③:2段階で動作確認する
まず、コンテナ自体が動いているかを確認します。
docker ps
voicevox という名前のコンテナが Up と表示されていればOKです。
ただしこれは「箱が動いている」ことの確認にすぎません。念のため、中のエンジンがきちんと応答するかも、VPS自身の内部から確かめておきましょう。
curl http://localhost:50021/speakers
キャラクターの一覧(JSON)が返ってくればOKです。ターミナルに大量のテキストが返ってきます。
docker ps では Up なのに curl が繋がらない場合、エンジンの起動処理がまだ終わっていない可能性があります。初回は数十秒かかることがあるので、少し待ってから再試行してください。それでも駄目なら docker logs voicevox でエラー内容を確認します。
これでVOICEVOXエンジンの準備は完了です。外からは一切見えませんが、VPSの内側では確かに動いている状態になりました。
Gitのヒヤリハット:コピー元を向いたままだった発覚
DockerとVOICEVOXの導入が終わったところで、次はローカルで構築したシステムを、これまでの記事と同じようにGitHubのリポジトリと連携させ、VPSにクローンしていきます。
しかし、ここで1つ、ヒヤリとする出来事が起きました。
コピーしていた大元のフォルダに、本番サービスのGit履歴がそのまま残っていたことが発覚したのです。
Tomoyaこれに気づいた瞬間、頭の中は「?」でいっぱいになりました。
AIに相談しながら、現状を把握するためのコマンド(git remote -v)を打ってみたところ、驚くべきことに、コピー元の本番リポジトリと、そのまま連携された状態になっていました。
つまり、このままGitにプッシュしてしまうと、コピー元の本番リポジトリが、VPS用に改造したシステムの内容で上書きされてしまう
既存の本番サービスを壊しかねない、大きな問題だったのです。
Tomoyaこれに気づいた瞬間は、さすがに背筋が凍りました。
気づいたきっかけは、GitHub上にVPS用の新しいリポジトリを作って連携させようとした際、「既に連携されています」というエラーが表示されたことでした。
作ったばかりのリポジトリが、いきなり連携済みになっているはずがありません。
Tomoya何かがおかしいと感じ、すぐにAIに相談することにしました。
CursorのIDE上に残っていたGitの更新履歴からも、この違和感に気づく手がかりを得られました。
原因を直した上で改めてプッシュし、正しくVPS用のリポジトリに反映されていることを確認して、この件は一件落着です!

Next.js側をVPSにデプロイする
ここも、以前の記事で行ったのと同じように、GitHubからVPSへcloneし、デプロイしていきます。
ローカルで見つけていた「危険なフォールバック(本番のCloud Runへ意図せず向いてしまう設定)」は、VPSの環境変数の中でも明示的に排除しておきました。
clone → 流れ変数を設定 → ビルドの3ステップ
慣れている方はコマンドだけ拾っていただければ進められます。
cd ~
git clone 新しく作ったGithubのリポジトリURL VPS_SS(作業ネーム/任意)
cd VPS_SS/frontend
ここだけ注意
コピー元のリポジトリと混同しないよう、必ず新しく作ったリポジトリのURLを使ってください。
また今回は、プロジェクトが frontend の中にある構成のため cd で1段階入っています。
実際のフォルダ構成に合わせて調整してください。

nano .env.local
GEMINI_API_KEY="発行したAPIキー"
VOICEVOX_ENGINE_URL="http://localhost:50021"
さきほど触れた「Cloud Runへの危険なフォールバック」を防ぐため、VOICEVOX_ENGINE_URL は必ず明示的に指定してください。
VOICEVOXのコンテナはVPS自身の中で動いているので、localhost(このVPS自身)でアクセスできます。
STEP3:ビルドする
npm install
npm run build
echo $?
0 が返ってくればビルド成功です。
tsconfigのVPSとの不一致によって「echo $?」で「1」が返る問題が出る方は以下を参考にしてください

git pullからnpm run buildまでの一連の流れは、思ったよりもスムーズに進めることができました。
これまで何度も繰り返してきた経験が、少しずつ積み上がってきているのを実感します。まだ基本的な理解に留まっている部分は多いと思いますが、次に進むためのやり方が自分の中で見えてきたことは、VPSを続けていく上での励みになり、気持ちも上向いてきている感じがあります。
pm2とDocker、2つの異なる永続化の仕組みを併用する
Tomoyaここでは、新しい経験をすることになりました。
①フロント(Next.js)側はpm2で、②VOICEVOXエンジンはDockerで、それぞれ常時起動させておく、という構成です。
「これまでとは違う仕組みを、同時に扱う」ということへの戸惑いはありましたが、自分の中でそれぞれの役割をイメージしながら整理することで、理解できたのは良かったです。
pm2で常駐化する
pm2 start npm --name voice-app -- start -- -p 3002
pm2 list


ポート開放は最小限に
ここも、これまでの記事と同様、外部(ブラウザ)からアクセスできるように、ポートとファイアウォールを許可していきます。もちろん、開放したのはフロント側(Next.js)の3002番だけです。
VOICEVOX側のポートは、先ほど決めた通り、公開しない方針のままにしています。
①Xserver VPS パケットフィルター設定にてPortの解放

②ターミナルにてファイアウォールを許可
VOICEVOXエンジン(50021番)は、さきほど127.0.0.1限定にしたので、外部公開の設定は一切不要です。
sudo ufw allow 3002/tcp
ブラウザでの動作確認、そして達成感
http://サーバーのIPアドレス:3002/dashboard
少しドキドキしながらブラウザを開きましたが、無事にUIが表示された瞬間、まず素直な喜びがありました。続けて、ローカルの時と同じように、頭であるGeminiを経由してVOICEVOXの音声生成がちゃんと返ってくるかを確認していきます。
ここも無事に返ってきたことに、また喜びを感じました。
Tomoyaエラーもなく、一通りの流れを確認できたことに、心底安堵しています。

TomoyaこれがDockerと同じような役割を果たすコンテナ技術を使っているサービスです!
※自分が運営しているサービスです
まとめ・次回予告
Tomoyaこれで、単発のやり取りがVPS上で動く状態にはなりました。
しかし、まだ「VPSである意味」には到達できていないと感じています。
もう一度AIに相談し、「VPSであるからこそ活きるシステムにしたい」という思いを伝えながら、次の方向性を検討していきました。
次回は、MVPとして完成したこのVPSチャット音声AIを、さらにテーマに近づけて改良していく記事になります。VPSならではのシステムに近づけるために、引き続きAI駆動で進めていきます。

以下のリンクから月額プランを契約すると、初月は半額で利用できます。コストが心配な方でも、最初の1か月は最低10ドルから試せるので、気軽に始めやすいと思います(解約も簡単にできます)。
他のVPSは「さくらインターネットから!」

前回のおすすめ記事はこちらから



VPSを使った24時間稼働システムを作成した記事はこちら



コメント
コメント一覧 (1件)
[…] 【非エンジニアが挑む】VPSにDockerを導入|「あえて公開しない」という設… […]