【実録】ConoHa VPS × GitHub Actionsで当サイトを公開するまで|手順書通りに進まなかった5つのつまずきと対処
本記事にはプロモーション(広告)が含まれます
📋 この記事の目次
このサイト「エンジニア羅針盤」(nanade.net)は、ConoHa VPS + Nginx + Let’s Encrypt + GitHub Actionsという構成で構築・公開しています。この記事は、その構築作業を実際にやってみた記録です。
先に手順書を作ってから作業に臨みました。それでも、実際には5箇所でつまずきました。「サーバー内部では正常なのにSSHが繋がらない」「設定を直したはずなのに反映されない」「ドメインが一時的に完全にダウンした」といった、事前の手順書には書けなかったトラブルです。
想定している読者は2層います。1つは、自分でもサイトやブログを個人で立ち上げて公開してみたい方。もう1つは、CI/CDやLinuxサーバーの運用が実際どんなものか、学習の一環として知っておきたい方です。理想的な手順書ではなく「実際に何が起きたか」の記録にこそ価値があると考え、つまずきも含めてそのまま公開します。
結論|この構成で月数百円・push一発で公開できる(ただし平坦ではなかった)
最終的に落ち着いた構成は以下の通りです。
- VPS: ConoHa VPS(メモリ2GB・CPU3コア・SSD100GB・36ヶ月契約)
- OS: Ubuntu 26.04 LTS(36ヶ月契約の期間中サポートが続く見込みのバージョンを選択)
- Webサーバー: Nginx
- HTTPS: Let’s Encrypt(certbot + certbot-nginxプラグインで自動更新まで設定)
- デプロイ: GitHub Actions(
mainブランチへのpushで自動ビルド・VPSへrsync転送)
料金について補足します。1GBプランは月466円ですが、今回契約したのは2GBプランで月650円です(466円 + 184円)。容量単価で見ると2GBの方が割安で、CPUも3コアと余裕があるため、184円上乗せして2GBを選びました。ただしこの金額は契約時点(2026年7月)に実施されていたキャンペーン価格です。 通常価格とは異なる可能性があるので、実際に契約する際は最新の料金をConoHa公式サイトで確認してください。
この構成が組み上がった後は、記事を書いてpushするだけで数分後には本番サイトに反映されます。運用が軌道に乗ってからの体験は、想像以上に快適です。
ただし、そこにたどり着くまでの道のりは平坦ではありませんでした。以下、実際に何が起きたかを順番に記録します。
構成の選定|なぜAstro + ConoHa VPSなのか
このサイトはAstro(静的サイトジェネレーター)でビルドしたHTMLを、Nginxで配信する構成です。採用理由は2つあります。Markdownで記事を書けてビルド・デプロイまで自動化しやすいこと。そして、静的ファイル配信中心のサイトはSEOとの相性がよいことです。
正直に書いておきます。「公開するだけ」が目的なら、VPSを使わずNetlify・Vercel・Cloudflare Pagesのようなホスティングサービスを使う方が、設定は間違いなく簡単です。サーバーのセキュリティ設定もOSのアップデートも、サービス側が面倒を見てくれます。
それでも今回VPSを選んだのには理由があります。1つは、WordPressのようにDBやPHPの管理を伴う構成ではないため、素のLinuxサーバーでも十分扱えるはずという判断です。もう1つは、DNS・Webサーバー・証明書・デプロイまでの各層を、一度は自分の手で通しておきたかったからです。
「VPSが最速・最良の選択」という話ではありません。あくまで今回の目的と学習価値に基づく選択だった、とご理解ください。
Part 1|VPS初期設定──最初の2つのつまずき
ConoHa VPSの管理画面から契約しました。OSは22.04・24.04・26.04から選べましたが、36ヶ月契約のサポート期限を考えて最新の26.04を選択しています。アプリケーションイメージ(KUSANAGI・LAMP・Dockerなど)は、今回は静的ファイルをNginxで配信するだけなので選びませんでした。
契約直後の小ネタ|ステータスが「起動中」から変わらない
契約直後、管理画面のステータスが「起動中」のまま長時間変化しないことがありました。ですが、コンソール(VNC経由)からはログインできており、起動自体は完了していました。管理画面の表示更新が遅れているだけのことがあります。コンソールでログインできれば、起動は完了したと判断してよいようです。
SSH鍵の登録|コンソールへの複数行貼り付けが途中で止まる
SSH鍵を作成してサーバーに登録する作業でも、地味なところでつまずきました。
ConoHaのブラウザコンソールに複数行のコマンドをまとめて貼り付けたところ、公開鍵という長い文字列を含む行の途中でカーソルが止まり、進まなくなったのです。疑似キー入力の処理が、長い行で詰まったと考えられます。
対処はシンプルです。コマンドを1行ずつ、プロンプトが返ってくるのを確認してから次を送る方式に変えたところ、解決しました。
つまずき①|サーバー内部は正常なのに、外部からSSH接続できない
鍵をサーバー側に登録し、systemctl status sshはactive (running)と表示されていました。それにもかかわらず、外部からssh root@(サーバーのIPアドレス)を実行するとOperation timed outになりました。
原因は、セキュリティグループの割り当てにありました。新規作成したサーバーのネットワークインターフェースには、SSHを許可しない「default」というセキュリティグループが割り当てられていたのです。SSH許可用の「IPv4v6-SSH」というグループ自体はConoHaに用意されていましたが、サーバー側に紐付いていませんでした。
対処は管理画面での設定変更だけです。ConoHaの管理画面 → サーバー詳細 → 「ネットワーク情報」タブで、割り当てるセキュリティグループを「IPv4v6-SSH」に変更したところ、即座に接続できるようになりました。
🧓 ベテランの現場コラム|エラーメッセージは「どのレイヤーで起きたか」を教えてくれる
Operation timed outというエラーは、実は結構な情報量を持っています。sshdの設定や鍵が間違っている場合は「接続拒否」や「認証失敗」という別のエラーが返るはずで、timed outはそもそもTCP接続自体が確立できていないことを示します。つまり、サーバー内部のsshdの設定を疑う前に、その手前のネットワークレイヤー(ファイアウォールやセキュリティグループ)を疑うべきサインだったわけです。新規VPSでSSH/HTTPが繋がらない場合は、まずサーバー内のufwより先に、クラウド側のセキュリティグループの割り当てを確認することをおすすめします。
つまずき②|設定ファイルを直したのに、パスワードログインが禁止されない
/etc/ssh/sshd_configのPasswordAuthenticationをnoに書き換えて再起動しても、実際にはパスワードログインが禁止されませんでした。
原因は、別の設定ファイルによる上書きでした。Ubuntuのcloud-init(サーバー初期化ツール)が/etc/ssh/sshd_config.d/50-cloud-init.confというファイルを生成しており、そこに明示的にPasswordAuthentication yesと書かれていたのです。sshdはsshd_config.d/配下のファイルを本体のsshd_configより優先して読み込みます。そのため、この上書きファイルの方が効いていました。
対処として、この上書きファイルも同じ内容に修正し、再起動することで解決しました。
なお、この種の設定変更では注意点が1つあります。設定を変えたら、必ず鍵ログインが引き続き成功するかを確認してから次の作業に進んでください。確認を怠って進めると、自分自身がサーバーから締め出されるリスクがあります。
ファイアウォール設定(ufw allow 80 / 443)とNginxのインストールは、特につまずくことなく完了しました。
Part 2|ドメインとHTTPS──サイトが一時的に消えた日
Part 2で最も深刻だったのが、次のDNSトラブルです。作業中、一時的にnanade.netがどこにも解決されない完全なダウン状態になりました。
つまずき③|DNSに「Ver.2.0」と「Ver.3.0」の2つの別システムがあった
ConoHaには、古い「DNS Ver.2.0」用の管理画面と、新しい「DNS Ver.3.0」用の管理画面が別々に存在していました。
今回のドメインは、元々Ver.2.0のシステム(ネームサーバーがns-a1/a2/a3.conoha.io系)で運用されていました。ところが、誤ってVer.3.0側の画面(ネームサーバーがa.conoha-dns.com / b.conoha-dns.org系)でAレコードを編集してしまったのです。値自体は正しく保存されました。しかし、実際の委任先(レジストリに登録されたネームサーバー)はVer.2.0側のままだったため、変更が一切反映されませんでした。
さらに事態が悪化しました。Ver.2.0側の画面で「ドメイン削除」を実行してしまい、Ver.2.0のDNSゾーンが完全に消えたのです。結果、ドメインがどこにも解決されない、完全なダウン状態になりました。
最終的な解決策は、委任先の切り替えでした。ドメインのネームサーバー委任先そのものを、Ver.2.0側からVer.3.0側(正しいAレコードが既に入っている方)に切り替えたのです。ConoHaの「ドメイン」メニュー(DNS管理とは別の場所にあります)から変更でき、変更は即座に反映されました。
補足|「ドメインを削除しますか?」という不穏なダイアログ
編集作業の途中、Aレコードを変更しようとした際に「ドメインを削除しますか?」という確認ダイアログが出たことがありました。ConoHaのサポートページで確認したところ、DNSレコードの削除自体はドメインの登録(所有権)には影響しないとのことでした。とはいえ紛らわしい表示なので、進める前に内容をよく確認することをおすすめします。
つまずき③から得た3つの教訓
- ConoHaで長期間運用しているドメインは、Ver.2.0システムで管理されている可能性がある。新しい管理画面(Ver.3.0)で編集しても反映されないことがある
- DNS変更作業の前には、必ず
dig ドメイン名 NS +shortで実際の委任先ネームサーバーを確認し、そのネームサーバーに対応する管理画面で編集する @(ルートドメイン)とwwwのAレコード、両方の保存操作を忘れずに行う
トラブル時に役立った確認コマンドは以下の3つです。
dig ドメイン名 NS +short # 実際の委任先ネームサーバーを確認
dig @ネームサーバー ドメイン名 A +short # そのネームサーバーに直接問い合わせ(キャッシュ回避)
dig +trace ドメイン名 A # 委任チェーン全体を追跡
Nginxのドメイン設定
DNSが落ち着いた後のNginx設定は次の通りです。
cat > /etc/nginx/sites-available/nanade.net << 'EOF'
server {
listen 80;
server_name nanade.net www.nanade.net;
root /var/www/nanade.net;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
EOF
ln -sf /etc/nginx/sites-available/nanade.net /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx
中身が空の状態では403 Forbiddenが返るのが正常です。404ではありません。try_filesはディレクトリの存在は確認できても、indexファイルが無いため403になります。ここで403が返ってくれば、正しく設定できているサインと考えて安心してよいです。
HTTPS化はあっさり完了
HTTPS化(certbot --nginxによるLet’s Encrypt証明書の取得)は、ここまでのトラブルが嘘のように、つまずきなく完了しました。証明書の自動更新タイマーも正常に設定されました。
Part 3|GitHub Actions自動デプロイ──最も手間取ったSSH鍵問題
自分用のSSH鍵とは別に、自動デプロイ専用の鍵を新規作成し、VPS側のauthorized_keysに登録しました。
GitHub側には、Settings → Secrets and variables → Actionsから、サーバーのIPアドレス・ユーザー名・デプロイ先ディレクトリ・秘密鍵の4つをSecretsとして登録します。GitHub Secretsは一度保存すると値を二度と閲覧できません。間違えたら「Update」で上書きするしかない点に注意してください。
つまずき④|Node.jsのバージョン不足でビルドが失敗
ワークフローのテンプレートではnode-version: 20にしていました。ですが実行時に、次のエラーでビルドが失敗しました。
Node.js v20.20.2 is not supported by Astro!
Please upgrade Node.js to a supported version: ">=22.12.0"
node-version: 22に変更して解決しました。
手順書を作った時点ではNode.js 20で足りていました。しかし、その2日後の実作業までの間にAstroのバージョンが上がり、要求バージョンが変わっていたのです。この手のツールのバージョン要件は生ものなので、ビルド失敗時はまずバージョン不足を疑うとよさそうです。
つまずき⑤|デプロイ用SSH鍵が読み込めない(最も手間取ったトラブル)
burnett01/rsync-deployments@7.0.1というアクションでVPSへrsync転送する設定にしたところ、次のエラーでデプロイが失敗し続けました。
Error loading key "(stdin)": error in libcrypto
Permission denied (publickey).
原因の切り分けは、次の順で進めました。
- まずed25519形式の鍵で試す → 失敗
- 「ed25519曲線はPEM/PKCS1形式で表現できない」という仕様を確認し、RSA 4096bit・PEM形式で鍵を作り直す → それでも同じエラー
openssl rsa -checkでローカルの鍵自体は正常と確認。つまり鍵そのものではなく、GitHub Secretsへのコピー&ペースト時に改行や文字コードの変換が起き、値が壊れていた可能性が濃厚に- アクション自体を、より枯れた
webfactory/ssh-agent(+標準のssh-add)に変更 → 同じ「error in libcrypto」エラーが再現。これで、特定アクションの不具合ではなく、GitHub Secretsに保存された値自体の破損が原因と断定
最終的な解決策は、秘密鍵をBase64エンコードして1行の文字列にしてから登録する方式でした。
base64 -i ~/.ssh/deploy_key | tr -d '\n'
改行や記号による事故が起きにくいこの1行の文字列をSecretsに保存し、ワークフロー側でデコードするよう変更したところ、あっさり解決しました。
- name: SSH鍵を復元(Base64デコード)
run: |
mkdir -p ~/.ssh
echo "${{ secrets.VPS_SSH_KEY }}" | base64 -d > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan -H ${{ secrets.VPS_HOST }} >> ~/.ssh/known_hosts
- name: VPSへ転送(デプロイ)
run: |
rsync -avzr --delete \
-e "ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=yes" \
dist/ ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }}:${{ secrets.VPS_TARGET_DIR }}
🧓 ベテランの現場コラム|エラーメッセージは犯人ではなく証言者
「error in libcrypto」というエラーメッセージだけを見ると、暗号ライブラリ自体の不具合のように思えます。ですが実際には「鍵ファイルのフォーマットが壊れている」ことを示す証言に過ぎませんでした。ここで効いたのは、鍵の形式を疑う→鍵自体の正しさを確認する→アクションを変えて再現するかを確認する、という順で1つずつ変数を切り分けていくやり方です。マルチライン(複数行)の秘密鍵をGitHub Secretsのようなテキストフィールドに保存する場合は、最初からBase64エンコードして1行にしてから貼り付ける方が、コピー&ペースト時の改行崩れによる事故を避けられます。道具がAIに変わっても、「表面のエラーに飛びつかず、1変数ずつ切り分ける」という現場の作法自体の価値は変わらないと感じました。
動作確認
動作確認はgit commit --allow-empty -m "デプロイテスト"で空コミットをpushして行いました。
なお、pushしてもワークフローが数分間「queued(順番待ち)」のまま進まないことがありました。これはGitHub側のランナー割り当ての遅延であり、こちら側の設定不備ではありません。気長に待つとよいです。
公開して初めて分かったこと|記事間リンクの404問題
各記事のfrontmatterでdraft: trueをfalseに変更してpushするだけで、Part 3で組んだパイプラインにより自動的に本番へ反映されます。ここまでは想定通りでした。
ただ、実際に公開してみて初めて気づいた問題がありました。すでにdraft: falseで公開済みだった記事の本文中に、まだdraft: trueのままだった別記事へのリンクが含まれていたのです。そのため、読者がクリックすると404になっていました。
対処は2段階で行いました。まず、リンク先の記事も公開状態に変更しました。その上で、CTAボタンの運用方針自体を見直しました。
当初は、ASP(アフィリエイト提携)の承認が下りるまではhref="#"のダミーリンクにしておく運用でした。この方式ではこの手の404が起きやすいため、次のように変更しています。ASP未承認の間でも各社の公式サイトへの非アフィリエイトの直リンクにしておき、承認が取れ次第、同じ場所のURLをアフィリエイトリンクに差し替える、という運用です。
ここから得た教訓はシンプルです。記事を1本ずつ個別に公開判断すると、既に公開済みの記事からのクロスリンクが壊れることがあります。複数の記事が相互にリンクし合っている場合は、関連記事をまとめて公開判断するか、公開前に内部リンク先がすべて公開済みかどうかを確認しておくと安全です。
障害対応クイックリファレンス(保存版)
今回遭遇したトラブルを一覧にまとめました。同じ症状に出会った際の、最初の当たりをつける用途で使ってください。
| 症状 | 疑うべき箇所 | 確認コマンド |
|---|---|---|
| SSH/HTTPが外部から繋がらない(サーバー内部では正常) | クラウド側のセキュリティグループ未割当 | 管理画面のネットワーク情報タブを確認 |
| sshd_configを直しても設定が反映されない | /etc/ssh/sshd_config.d/配下の上書きファイル |
grep -rn "設定項目名" /etc/ssh/sshd_config* |
| DNSを変更しても反映されない | ネームサーバーの委任先と編集している画面が別システム | dig ドメイン NS +shortで実際の委任先を確認 |
GitHub Actionsのビルドがnot supported系エラーで失敗 |
Node.js等のツールのバージョン不足 | エラーメッセージ内の要求バージョンを確認しワークフローを更新 |
SSH鍵を使うActionsがerror in libcryptoで失敗 |
GitHub Secretsへのペースト時の改行・文字化け | 鍵をBase64エンコードして1行にしてから保存 |
| 公開済みページのリンクが404 | リンク先記事がまだdraft: true |
リンク先も含めて公開状況を確認 |
まとめ|「理論通りに進まない」ことこそが学び
今回のようなトラブルは、これまでなら相応の時間がかかっていたはずです。セキュリティグループの割り当て漏れ、cloud-initによる設定の上書き、DNSのVer.2/3分裂、SSH鍵のエラー。いずれも、自分でログを読み、検索し、値を変えては試す、という試行錯誤を要するものです。それが、AIと一緒に作業したことで、その日のうちにすべて解決までたどり着けました。
WordPressの構成(DBやPHPの管理が絡む分、技術的にはやることが多い)と比較しても、体感としては「大差ない」というのが正直な感想でした。ここから見えてくるのは、SE・プログラマ(インフラ系も含む)の役割が、これまでとは明確に変わっていくのではないかという予感です。
🧓 ベテランの現場コラム|AI時代、エンジニアの価値は「直す力」から「判断する力」へ
変わるのは「役割がなくなる」ことではなく、価値の重心が「実行・トラブルシューティング」から「判断・レビュー」に移ることだと捉えています。
今回の作業でAI(Claude Code)が担っていたのは、ログを読み、仮説を立てて直すという「作業」の部分でした。一方で、VPSのスペックや契約年数をどう選ぶか、「ドメイン削除」という不穏な表示を見て一旦手を止める危険察知、ConoHaの画面操作そのもの、アフィリエイトリンクの発行といった自分のアカウントに紐づく行為、CTAをダミーにするか実リンクにするかというビジネス判断、記事を公開してよいかどうかの最終判断──これらは、その場の状況判断と、実世界への権限・責任が必要な行為であり、最後まで人間の側に残っていました。
「ログを読んでエラーメッセージを検索し、値を直す」という修行的な作業の価値は、これから急速に下がっていくはずです。一方で、「何を作るべきか」「これは安全か」「AIの成果物は正しいか」を判断できる人の価値は、相対的に上がっていくと感じています。
※本記事の構成・料金・トラブルシューティングの内容は、2026年7月9日の実際の構築作業の記録に基づいています。ConoHa VPSのプラン内容・料金や、GitHub Actions・各ツールのバージョン要件は変動します。最新の情報は必ず各公式サイトでご確認ください。