自作のToDoアプリ(React + FastAPI + PostgreSQL)を、Oracle Cloudの無料枠「Always Free」で立てたVPSに載せて、自分のPCをシャットダウンした状態でも24時間常時稼働するようにしてみた記録です。
サーバーを触ったことがない人にもわかるよう、用語の説明とつまずいたポイントを中心にまとめます。
VPSとは?他のサービスとの違い
VPS(仮想専用サーバー)は、データセンターにある大きなコンピューターを分割し、各領域を仮想的な専用サーバーPCとして貸し出しているサービスです。
イメージとしては「インターネットの向こうにある、自分だけのパソコンをレンタルする」感覚です。
| VPS | PaaS(Renderなど) | |
|---|---|---|
| 自由度 | 高い。OSから自由に設定できる。 | あまり高くない。しかしアプリを置くだけで簡単に公開できる |
| 手間 | ネットワーク・OS・ソフトの設定を全部自分でやる | 数クリックで終わる |
| 学べること | サーバーの仕組みを一通り体験できる | サーバーの中身はほぼ見えない |
今回は「できるだけ無料」「勉強も兼ねたい」の2つを条件に、あえてVPSを自分でゼロから構築しました。
この記事に出てくる主な用語
・SSH:自分のPCから、離れたサーバーを安全に遠隔操作する仕組み
・ポート:サーバーの「窓口番号」。Webは80番(http)と443番(https)を使う
・Nginx(エンジンエックス):Webサーバー。ブラウザからのアクセスを受け付ける窓口役
・リバースプロキシ:受け付けたアクセスを、URLに応じて裏側のアプリに振り分ける仕組み
・スワップ:メモリが足りないとき、ディスクの一部をメモリ代わりに使う仕組み
今回作った公開環境の全体像

- ブラウザからのアクセスは、まず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)を使いました。
構築の流れ
ネットワークとサーバーを作る
- Oracle Cloudのアカウントを作り、Always Freeの条件(ホームリージョン、対象のシェイプ)を確認
- VCN(仮想クラウド・ネットワーク=クラウド上の自分専用ネットワーク)を作成
- セキュリティリスト(通してよい通信を決めるファイアウォールのような設定)で、80番・443番ポートを開ける
- コンピュートインスタンス(仮想サーバー)を作成。OSは Oracle Linux 9、シェイプは
VM.Standard.E2.1.Micro - 作成時に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 Found | proxy_pass の末尾の / で /api/ が消えていた | 末尾の / を削除 |
systemdの起動で status=203/EXEC | SELinuxが .venv 内の実行ファイルの起動を拒否 | 実行ファイルのラベルを bin_t に変更 |
| Nginx経由のときだけ404 | SELinuxがNginxから8000番ポートへの接続をブロック | setsebool -P httpd_can_network_connect 1 |
| 見積もり画面に料金が表示される | 見積もり機能はAlways Freeの割引を反映しない仕様 | Cost Analysisで実際の請求額(0円)を確認 |
Certbotのインストールで No match for argument | EPELリポジトリ(追加ソフトの配布元)が無効のまま | 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。普段意識しない層の仕組みを学べたのが一番の収穫


コメント