作ったアプリを公開する段になると、置き場所を選ぶことになります。
PaaS(Railway、Render など) … コードを渡すと、あとはやってくれる
VPS(ConoHa など) … サーバーを1台借りて、自分で管理する
私は前者を選びました。理由ははっきりしていて、手元の Docker Compose の構成をそのまま持っていけると思ったからです。
結果を先に書くと、動かせませんでした。デプロイは成功するのに、アプリが起動しない。原因を突き止められないまま VPS に移し、そちらでは今も動いています。
しばらくは「運が悪かった」くらいに思っていました。ところが最近、Railway の公式ドキュメントをちゃんと読んで、自分の前提がそもそも間違っていたと分かりました。しかもそれは、ページのいちばん上に書いてありました。
この記事は、その記録です。Railway が悪いという話ではありません。読まなかった私の話です。
なぜ Railway を選んだのか
当時の判断は、自分のリポジトリに残っていました。要件定義のファイルです。
docs/requirements_02_system_architecture.md:43
ホスティング先:Railway(Docker Compose構成をそのまま持っていけるため採用。次点候補はRender)
docs/CLAUDE_addendum_en.md:16
Production hosting: Railway (preferred, can lift the existing Docker Compose
setup directly). Render is the fallback option.
日本語でも英語でも、同じことを書いています。採用理由は「Compose 構成をそのまま持っていける」の一点でした。
想定していた流れもこう書いてありました。
ローカル検証 → GitHubへpush → GitHub ActionsでCI → Railwayへ自動デプロイ
きれいな絵です。そして現在この通りには一切なっていません。今は VPS に手で入れています。
実際に起きたこと
デプロイは通りました。ビルドも成功します。でもアプリが起動しませんでした。
正直に書くと、ここから先を私は詳しく説明できません。
- 原因を特定していません
- 当時のログも残っていません
しばらく粘って、諦めて VPS を借りました。この記事で「原因はこれでした」と書けたら読みやすいのですが、書けるだけの材料が手元にありません。後半でこの点にもう一度戻ります。
公式ドキュメントを読んだら、最初の行に書いてあった
今回この記事を書くにあたって、Railway の公式ドキュメントを開きました。「Deploy a Docker Compose App to Production」というページです。
Railway does not run
docker-compose.ymlfiles directly. Instead, each service defined in your Compose file maps to a separate Railway service within a project. The sections below walk through that translation so you can take an existing Compose setup and deploy it to production without managing servers.
訳すとこうなります。
「Railway は docker-compose.yml を直接実行しません。かわりに、Compose ファイルで定義された各サービスが、プロジェクト内の個別の Railway サービスに対応づけられます。以下のセクションでは、その翻訳の手順を説明します」
一文目で終わっています。そのまま実行しないと書いてあります。
そして注目したいのが三文目の translation という単語です。公式自身が、この作業を翻訳と呼んでいます。私が採用理由に書いた「そのまま持っていける」の、ちょうど反対語が公式ドキュメントの中にありました。
公式が示している対応表
同じページに、Compose の書き方が Railway では何になるかの対応表があります。以下は原文をそのまま引用したものです(訳は付けず、この下で説明します)。
| Compose concept | Railway equivalent |
|---|---|
services: entry | Railway service |
build: | Deploy from GitHub repo with a Dockerfile |
image: | Deploy from a Docker image |
ports: | Public networking (external) or automatic private networking (internal) |
volumes: | Railway Volumes |
networks: | Automatic private networking (no config needed) |
environment: / env_file: | Railway Variables |
depends_on: | No direct equivalent. Applications should handle connection retries at startup. |
左の列は、Compose ファイルに書く項目です。右の列が Railway 側での対応物です。
ほとんどの行に「対応するものはある」と書いてあります。移行できないわけではありません。そして、すべてを手で設定するしかないわけでもありません。
2026年9月8日に確認した公式には、Compose ファイルの取り込み機能もあります。 キャンバスへファイルをドラッグすると、サービスとマウントされたボリュームが、反映前の変更(staged changes)として取り込まれます。ただし、すべての Compose 設定に対応するわけではないとも明記されています。取り込めることと、Compose をそのまま実行できることは別です。 接続先や起動時の処理などは、対応表と照らして確認します。
私はこの機能を試していません。2026年6〜7月の時点で使えたかも未確認で、当時の失敗原因を説明する材料にはしません。出典は Railway の Dockerfiles ドキュメントです。
そして最終行だけ、書いてあることの質が違います。
とくに効いてくる3つの記述
1. depends_on に相当する機能がない
The
depends_ondirective has no Railway equivalent. If the web service starts before the database is ready, it should retry the connection. Most database client libraries and ORMs support this through connection retry configuration.
「depends_on ディレクティブに Railway での相当物はありません。Web サービスがデータベースの準備完了より先に起動した場合、接続を再試行すべきです。ほとんどのデータベースクライアントライブラリと ORM は、接続リトライの設定でこれに対応しています」
depends_on は Compose で「このプログラムはデータベースが起動してから立ち上げてね」と順番を指定する書き方です。Railway にはこれがないので、アプリ側で「つながらなかったらもう一度試す」処理を書いておく前提になります。
ここで注目してほしいのは、公式が「無い」で終わっていないことです。対処法まで書いています。この記事の結論に関わるので、覚えておいてください。
2. プライベートネットワークにはポートマッピングの層がない
Note: Use the port your application actually listens on when connecting over the private network. There is no port mapping layer.
「注意:プライベートネットワーク経由で接続する際は、アプリケーションが実際に待ち受けているポートを使ってください。ポートマッピングの層はありません」
Compose では ports: "80:80" のようにホスト側とコンテナ側のポートを対応づけます。一方、この引用は Railway のプライベートネットワーク内の接続の説明です。そこではアプリが実際に待ち受けている番号を使います。公開ネットワークまで含めて「Railway にはポートの設定がない」と読むのは広げすぎです。
3. サービス同士の呼び名が違う
Compose では、サービス名をそのままホスト名として使えます。db という名前のサービスなら db で届きます。Railway では <service-name>.railway.internal という形になります。
さらに、データベースへの接続情報は reference variables という仕組みを使うことが勧められています。公式はこう書いています。
Example: instead of hardcoding
DATABASE_URL=postgres://user:pass@db:5432/myapp, create a reference variable that pulls the value from the Postgres service'sDATABASE_URLvariable.
「例:DATABASE_URL=postgres://user:pass@db:5432/myapp と直接書き込むかわりに、Postgres サービスの DATABASE_URL 変数から値を引く参照変数を作ってください」
instead of hardcoding——直接書くのはやめて、と名指しされています。そして名指しされているその書き方は、Compose で普通に使う書き方です。
原因は、今も特定していません
ここが、この記事でいちばん正直に書きたいところです。
上の3つは、私が遭遇した「デプロイは通るのにアプリが起動しない」という症状と、きれいに噛み合います。
| 公式の記述 | 症状との噛み合い |
|---|---|
depends_on に相当機能がない | 各サービスが独立に起動するので、DBの準備前にアプリが立ち上がって落ちうる |
| プライベートネットワークにはポートマッピング層が無い | 実際の待受ポートを接続先に指定していなければ、繋がらない可能性がある |
| ホスト名の解決が別物 | postgres://…@db:5432/… のような、Compose のサービス名を書いた接続文字列は解決できない |
読んだとき、正直「これだったのか」と思いました。
でも、それは書けません。
当時のログが残っていない以上、この3つのどれかが原因だったと証明する材料が私の手元にありません。噛み合う説明が立つことと、原因が分かったことは違います。
「原因は depends_on でした」と書けば、記事としてはずっと読みやすくなります。でもそれは、確かめていないことを確かめたように書くことになります。
設計時の前提を確認していませんでした
整理するとこうなります。
- 私は「Compose をそのまま持っていける」という理由で Railway を選んだ
- その前提は、公式ドキュメントの一文目で否定されている
- 動かなかった具体的な原因は分からない。ただし公式の記述と症状は噛み合う
- 現在の公式には、取り込みの範囲と、移行時に確認することが書かれている
4 が大事だと思っています。公式ドキュメントは、対応表を出し、手順を追い、depends_on については対処法まで書いています。ただし、読めば私のアプリも必ず移行できた、とまでは言えません。確認すべき前提を知ることと、移行を成功させることは別です。
私がやったのは、「Compose が使えるらしい」という理解のまま設計を確定させ、要件定義に書き、そのまま進めたことです。確認していないことを、確認したつもりで前提にしていました。
VPS に移して、直接触る運用になった
移した先は ConoHa VPS の2GBプランです。2026年8月13日の記事には、VPSの uptime が46日だった時点のメモリ実測記録があります。各コンテナが46日間止まらず動いたという意味ではありません。今回、この稼働日数を測り直したわけではありません。
→ VPSは2GBで足りるか — 稼働46日目の平常時を実測
移してから気づいたのは、私の運用の仕方が、そもそもサーバーに入る前提でできていることでした。
# 記事の本文をデータベースの中で直接書き換える
sudo docker compose ... exec -T backend python manage.py shell
# データベースのバックアップを取る
sudo docker compose ... exec -T postgres pg_dump ...
# 設定ファイルが最終的にどう解釈されるかを確認する
docker compose -f docker-compose.yml -f docker-compose.prod.yml config
どれも「SSH で入って、自分の Docker を直接触る」形です。これは PaaS が得意な形ではありません。
だから今の結論は「Railway より VPS が優れている」ではなく、「私の手の動かし方には VPS が合っていた」です。逆に、サーバーに入らない・入りたくない運用なら、PaaS のほうがずっと楽です。
選ぶ前に、どこを読めばいいか
同じ失敗をしないために、私が今やるならこうします。
1. 使う予定の構成の名前で、公式ドキュメントを検索する
「Docker Compose」でも「Django」でも構いません。自分が持っていこうとしているものの名前で検索してください。
Railway の場合、その名前で検索すれば今回のページに着きます。一文目で分かるのは、Compose ファイルを直接実行する仕組みではないという点です。その先の対応表と取り込み機能の範囲まで確認します。
2. 「対応表」を探す
対応表があれば、自分の構成のどの項目が何に置き換わるのかを確認します。取り込み機能があっても、未対応の設定まで移せるとは限りません。表の存在だけで互換性を判定せず、取り込み対象と残る作業を読みます。
3. 相当物がない項目と、取り込まれない設定をメモする
対応表の中で、No direct equivalent(相当物なし)と書かれた行が、あなたが自分で手当てしなければならない箇所です。今回でいえば depends_on の行がそれでした。
4. どちらも試せるなら、小さく試す
後戻りできる条件は、事業者ごとに違います。2週間試せるところ、お試しは無いとFAQに明記しているところがあります。無料VPSの案内があっても、いま申し込めるとは限りません。実際、XServer VPS は2026年9月時点で全プランの新規申込を停止中です。最低利用期間も、縛りが無いところから3ヶ月のところまで分かれます。縛りが無い側にも、長期割引を選ぶと途中解約できなくなる形があります。
4社ぶんの公式の記述を並べて、条件ごとに絞った表が別の記事にあります。試してから決めたいなら、そちらを先に見てください。
⚠️ そこで書いたことを1つだけ持ってくると、「試せるか」と「やめられるか」は別の条件です。2週間試せても、本登録したあとに最低3ヶ月続くことがあります。私はこの2つを混ぜて考えていました。
まとめ
「そのまま持っていける」と書いてある構成でも、何が「そのまま」で何が「翻訳」なのかは、自分で確かめないと分かりません。
私の場合、最初に必要だったのは公式ドキュメントで前提を確認することでした。それをせずに設計を決めて、動かず、サーバーを借り直しました。ただし、文書を読めば今回の起動失敗を防げた、と証明できたわけではありません。
そしてこの記事にも、分からないままの部分が残っています。起動しなかった原因は、今も特定できていません。噛み合う説明はありますが、それは説明であって証拠ではありません。
次にやること
置き場所を決めかねているなら、今日この順番でやってみてください。
- 使う予定のサービス名 + 自分の構成名(例:
Railway docker compose)で公式ドキュメントを検索する - 移行の対応表と取り込み機能の対象を確認する
No direct equivalentの項目や、取り込まれない設定をメモする- 小さく試す場合も、試用条件・課金開始・最低利用期間・解約方法を確認する。1か月で終了できる契約条件の場合に限り、1か月だけ借りる案を検討する
サーバーを選ぶ話は、VPSは2GBで足りるか に実測値を載せています。プランで迷っているなら、そちらもどうぞ。
そして、借りたあとに必ず必要になるのが最初の設定です。借りた直後にどこまで外から届く状態なのかは事業者によって違うので、まずそこを確かめるところから始めます。ここは飛ばせません。
出典:Railway 公式ドキュメント「Deploy a Docker Compose App to Production」
https://docs.railway.com/guides/docker-compose(2026年8月13日 閲覧)
引用は原文のまま掲載し、訳を添えています。引用部分に、私が強調を加えた箇所はありません。
2026年9月8日追記:移行ガイドを再確認し、Docker Compose の取り込みの対象と制限を加えました。過去の障害原因や、当時の機能提供状況を再現・確認したものではありません。
