Oracle Cloudの無料VPSで自作Webアプリを公開してみた

自作のToDoアプリ(React + FastAPI + PostgreSQL)を、Oracle Cloudの無料枠「Always Free」で立てたVPSに載せて、自分のPCをシャットダウンした状態でも24時間常時稼働するようにしてみた記録です。
サーバーを触ったことがない人にもわかるよう、用語の説明とつまずいたポイントを中心にまとめます。


VPSとは?他のサービスとの違い

VPS(仮想専用サーバー)は、データセンターにある大きなコンピューターを分割し、各領域を仮想的な専用サーバーPCとして貸し出しているサービスです。
イメージとしては「インターネットの向こうにある、自分だけのパソコンをレンタルする」感覚です。

VPSPaaS(Renderなど)
自由度高い。OSから自由に設定できる。あまり高くない。しかしアプリを置くだけで簡単に公開できる
手間ネットワーク・OS・ソフトの設定を全部自分でやる数クリックで終わる
学べることサーバーの仕組みを一通り体験できるサーバーの中身はほぼ見えない

今回は「できるだけ無料」「勉強も兼ねたい」の2つを条件に、あえてVPSを自分でゼロから構築しました。

この記事に出てくる主な用語
・SSH:自分のPCから、離れたサーバーを安全に遠隔操作する仕組み
・ポート:サーバーの「窓口番号」。Webは80番(http)と443番(https)を使う
・Nginx(エンジンエックス):Webサーバー。ブラウザからのアクセスを受け付ける窓口役
・リバースプロキシ:受け付けたアクセスを、URLに応じて裏側のアプリに振り分ける仕組み
・スワップ:メモリが足りないとき、ディスクの一部をメモリ代わりに使う仕組み


今回作った公開環境の全体像

Oracle Cloud上の公開環境の全体像。ブラウザからのアクセスをNginxが受け、画面はReactの静的ファイル、/api/はFastAPIへ振り分け、FastAPIがPostgreSQLにつなぐ
  • ブラウザからのアクセスは、まずNginxが受け付ける
  • URLが / なら画面(Reactをビルドしたファイル)を返し、/api/ ならバックエンド(FastAPI)に転送する
  • FastAPIはsystemd(Linuxのサービス管理の仕組み)で常駐させ、サーバーを再起動しても自動で立ち上がるようにする
  • データベース(PostgreSQL)にはFastAPIだけがつなぎ、外部には公開しない

無料で使えるクラウドサーバーの比較

常時無料、または低コストで使える候補を比べました(2026年9月時点)。

サービス料金スペック目安注意点
Oracle Cloud Always Free無料(期限なし)ARM 2 OCPU / 12GBメモリ、またはAMD 1 OCPU / 1GBメモリ×2台2026年にARM枠が4 OCPU / 24GBから半減。人気で作成できないこともある
Google Cloud 無料枠無料(期限なし)e2-micro(2 vCPU / 1GB)対象は米国の特定3リージョンのみ。スペックは控えめ
Render(無料プラン)無料可変15分アクセスがないとスリープし、常時稼働ではない
Railway少額の無料クレジットのみ可変無料クレジットが少なく、常時稼働には向かない
AWS / Azure など新規登録時のクレジット高スペックも選べるクレジットに期限があり、ずっと無料では使えない

「常に動かせる」「無料」「それなりのスペック」を同時に満たせたのは、Oracle Cloud Always Freeだけでした。

OCPUとは?
Oracle CloudでのCPUの数え方です。ざっくり「CPUコアの数」と考えればOKです。


Oracle Cloud Always Freeを選んだ理由

  • スペックが高い:無料でARM 2 OCPU・12GBメモリが使えるのは、ほかにない水準
  • 期限がない:お試し期間ではなく、条件を守る限りずっと無料
  • 勉強になる:ネットワーク・OS・ミドルウェアまで自分で組む必要があり、学習目的に合っている
  • デメリットも承知:設定の手間、ARM枠の在庫切れ、見積もり画面が無料枠を反映しないことなどは、事前に調べたうえで選んだ

今回は、すぐに作成できたAMDのシェイプ(VM.Standard.E2.1.Micro:1 OCPU / メモリ1GB)を使いました。


構築の流れ

ネットワークとサーバーを作る

  1. Oracle Cloudのアカウントを作り、Always Freeの条件(ホームリージョン、対象のシェイプ)を確認
  2. VCN(仮想クラウド・ネットワーク=クラウド上の自分専用ネットワーク)を作成
  3. セキュリティリスト(通してよい通信を決めるファイアウォールのような設定)で、80番・443番ポートを開ける
  4. コンピュートインスタンス(仮想サーバー)を作成。OSは Oracle Linux 9、シェイプは VM.Standard.E2.1.Micro
  5. 作成時にSSHの鍵ペアを生成し、秘密鍵をダウンロードしておく

秘密鍵は「サーバーの合鍵」です。なくすとログインできなくなり、人に渡すと勝手にログインされます。大切に保管してください。

自分のPCからSSHでつなぐ

Windowsの場合、PowerShellからそのままSSH接続できます。
秘密鍵は ~/.ssh フォルダに置き、自分以外が読めないように権限を絞ります(権限がゆるいと、SSHが鍵の使用を拒否します)。

# 秘密鍵の権限を自分だけに制限(PowerShell)
icacls $HOME\.ssh\oracle_key /inheritance:r
icacls $HOME\.ssh\oracle_key /grant:r "$($env:USERNAME):(R)"

# サーバーに接続(Oracle Linuxの初期ユーザーは opc)
ssh -i $HOME\.ssh\oracle_key opc@<サーバーのIPアドレス>

メモリ不足対策にスワップを作る

最初に dnf update(OSのアップデート)を実行したところ、途中でメモリ1GBを使い切り、SSHごと応答しなくなりました。
サーバーを再起動したあと、2GBのスワップファイルを作って再発を防ぎました。

sudo dd if=/dev/zero of=/swapfile bs=1M count=2048
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# 再起動後も有効にする
echo '/swapfile none swap defaults 0 0' | sudo tee -a /etc/fstab
ポンコツプログラマ

メモリ1GBのサーバーでは、スワップはほぼ必須だと痛感しました…

必要なソフトを入れる

  • Nginx・Python・PostgreSQLを dnf(Oracle Linuxのパッケージ管理コマンド)でインストール
  • PostgreSQLでアプリ用のデータベースとユーザーを作成
  • 設定ファイル pg_hba.conf の認証方式を ident から md5 に変え、パスワードでログインできるようにする

identとmd5の違い
ident は「OSのユーザー名とDBのユーザー名が同じならOK」という方式です。アプリからパスワードでつなぐには、パスワード認証(md5)に変える必要があります。

アプリを転送して動かす

  • ローカルPCのアプリを、.venv や node_modules を除いてZIPにまとめる
  • scp(SSHを使ってファイルをコピーするコマンド)でサーバーに送り、解凍する
  • Pythonの仮想環境を作って依存ライブラリを入れ、uvicorn でバックエンドを起動。PostgreSQLへの接続とテーブルの自動作成を確認
scp -i $HOME\.ssh\oracle_key app.zip opc@<サーバーのIPアドレス>:~/

ここで、サーバーに入っていたPythonが3.9だったため、コードの int | None という型ヒントの書き方(Python 3.10以降の構文)がエラーになりました。
Python 3.11を追加でインストールし、仮想環境を作り直して解決しました。

Nginxで公開する

Nginxをリバースプロキシとして設定し、URLによって振り分けます。

server {
    listen 80;
    server_name _;

    # 画面(Reactのビルド結果)
    root /home/opc/todo-app/frontend/dist;
    location / {
        try_files $uri /index.html;
    }

    # API はバックエンドへ転送
    location /api/ {
        proxy_pass http://127.0.0.1:8000;
    }
}

proxy_pass のURLの末尾に / を付けると、転送時に /api/ の部分が消えてしまいます。今回はこれでAPIが404になりました。
・NG:proxy_pass http://127.0.0.1:8000/;
・OK:proxy_pass http://127.0.0.1:8000;

SELinuxの許可を出す

ここが一番の山場でした。設定は正しいのに、SELinuxが次の3か所をブロックしていました。

  • Nginxがホームフォルダ内の画面ファイルを読むこと
  • systemdが仮想環境(.venv)の中の実行ファイルを起動すること
  • NginxがバックエンドのポートへのAPI転送で接続すること
# 画面ファイルを Web 公開用のラベルに変更
sudo semanage fcontext -a -t httpd_sys_content_t "/home/opc/todo-app/frontend/dist(/.*)?"
sudo restorecon -Rv /home/opc/todo-app/frontend/dist

# 仮想環境の実行ファイルを「実行してよいファイル」のラベルに変更
sudo semanage fcontext -a -t bin_t "/home/opc/todo-app/backend/.venv/bin(/.*)?"
sudo restorecon -Rv /home/opc/todo-app/backend/.venv/bin

# Nginx から他のポートへの接続を許可
sudo setsebool -P httpd_can_network_connect 1

SELinuxとは?
Linuxのセキュリティ機能で、ファイルやプログラムに「ラベル」を付け、「このプログラムはこのラベルのファイルにしか触れない」と細かく制限します。ファイルの権限(chmod)が正しくても、ラベルが合っていないとアクセスを拒否されます。Oracle Linuxでは最初から有効になっています。

バックエンドを常駐させる

SSHを切ってもバックエンドが動き続けるよう、systemdのサービスとして登録しました。落ちても自動で再起動し、サーバー起動時にも自動で立ち上がります。

[Unit]
Description=ToDo API (FastAPI)
After=network.target postgresql.service

[Service]
User=opc
WorkingDirectory=/home/opc/todo-app/backend
ExecStart=/home/opc/todo-app/backend/.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now todo-api

ドメインを用意する

IPアドレスのままだと覚えにくく、サーバーを作り直すと変わってしまうこともあります。
そこで無料のDNSサービスDuckDNSでサブドメインを取得し、サーバーのIPアドレスに紐付けました。HTTPS化に使うCertbotとNginx用プラグインもインストール済みです(HTTPS化は次のステップ)。


つまずいたところ

起きたこと原因対処
dnf update 中にSSHごと応答しなくなったメモリ1GBを使い切った(メモリ不足)再起動して、2GBのスワップを追加
TypeError: unsupported operand type(s) for |サーバーのPythonが3.9で、3.10以降の書き方に対応していないPython 3.11を入れて仮想環境を作り直し
ブラウザで403 Forbiddenホームフォルダの権限と、SELinuxのラベルがNginxのアクセスを拒否chmod で通過権限を付け、SELinuxのラベルを変更
設定を変えてもNginxのテストページのままブラウザのキャッシュスーパーリロード(Ctrl+Shift+R)かシークレットウィンドウで確認
APIが404 Not Foundproxy_pass の末尾の / で /api/ が消えていた末尾の / を削除
systemdの起動で status=203/EXECSELinuxが .venv 内の実行ファイルの起動を拒否実行ファイルのラベルを bin_t に変更
Nginx経由のときだけ404SELinuxがNginxから8000番ポートへの接続をブロックsetsebool -P httpd_can_network_connect 1
見積もり画面に料金が表示される見積もり機能はAlways Freeの割引を反映しない仕様Cost Analysisで実際の請求額(0円)を確認
Certbotのインストールで No match for argumentEPELリポジトリ(追加ソフトの配布元)が無効のままdnf config-manager --set-enabled で有効化

ほとんどがSELinuxがらみでした。
「設定ファイルは正しいのに動かない」ときは、まずSELinuxを疑うのがおすすめです。

ポンコツプログラマ

SELinuxに何度も止められました(笑)


Oracle Cloudを使うときの注意点

  • Always Freeの条件は3つ同時に満たす:ホームリージョンで作る、対象のシェイプ(VM.Standard.E2.1.Micro や VM.Standard.A1.Flex)を選ぶ、ストレージを無料枠の範囲に収める。どれか1つでもずれると課金対象になる
  • 見積もり画面を鵜呑みにしない:Always Freeの割引が反映されないため、作成前の見積もりに金額が出ても実際は0円のことが多い。最終判断はCost Analysisで
  • 無料枠を超えた分は課金され得る:利用規約に超過分の支払い義務が書かれている。心配なら予算アラートを設定しておく
  • IPアドレスは変わることがある:インスタンスを作り直すと変わるので、DuckDNSなどでドメイン名を紐付けておくと安心
  • メモリ1GBは非力:スワップを作り、重い処理を同時に動かさない

今の時点では http:// のままで、通信が暗号化されていません。ログインのパスワードなどを扱うアプリなので、早めにHTTPS化する予定です。


まとめ

  • 無料で常時動かせるサーバーとして、Oracle Cloud Always Freeを選んだ
  • ネットワーク作成 → サーバー作成 → SSH接続 → ソフトのインストール → アプリ配置 → Nginx → systemd まで、一通り自分の手で構築した
  • つまずきの大半はSELinux。普段意識しない層の仕組みを学べたのが一番の収穫

コメント

タイトルとURLをコピーしました