プロジェクトが止まってた!
ハッカソンで作ったブックマーク管理アプリ(Next.js 15 + Supabase)を数ヶ月放置していたら、Supabaseの無料プロジェクトが一時停止していて、しかもダッシュボードから復活できない状態になっていました。なので今回はその体験談と、プロジェクトが停止しないようにするためにはどうすればいいかを紹介します。
事の発端:プロジェクトが動かない
久しぶりに自分のアプリを開いたら、エラーが出ていないのにログインができず、サインアップもできない状態になっていました。
Supabaseダッシュボードを開くと、以下のようなメッセージが表示されました。
The project "bookmark-manager" is currently paused
This project has been paused for over 90 days and cannot be restored through the dashboard.
一時停止(paused)は今まで何度かありましたが、今回は「90日を超えているのでダッシュボードからは復元できない」というところまで進んでいました。
Supabase無料プランの仕様
現在の公式ドキュメントで仕様を確認しました。2026年時点での仕様は以下の通りです。
1. 7日間の低活動で一時停止
Free Planのプロジェクトは、7日間にわたって活動が少ないとサーバーリソース節約のために一時停止されます。通常、過去1週間に毎日数件のユーザーリクエストがデータベースにあれば、プロジェクトが一時停止されることはないとのこと。
この際、supabaseからは2通のメールが届きます
1 警告メール:一時停止措置が実施される約1週間前に届くメール
2 確認メール:プロジェクトが一時停止された後に届くメール
警告メールを受け取った後に、supabaseのダッシュボードからプロジェクトにアクセスして、アクティビティを生成する等の操作を行えば、一時停止を回避することができます。
2. 一時停止後、90日間はダッシュボードから復元できる
プロジェクトが一時停止された後、90日間は復元できる猶予期間があります。この猶予があるのは、Supabase側がプラットフォームの変更を入れる余地を持たせるためで、古いバックアップが新しい仕様と互換でなくなる場合があるからだそうです。
3. 90日を超えると、ダッシュボードからは復元不可
そして今回の自分のケースです。90日の猶予を過ぎると、ダッシュボードからの復元はできなくなります。 ここまで来ると、新しいプロジェクトへ復元する必要が出てきます。
4. 「活動」の判定はDBへのリクエストが基準
1週間アクティブであるかどうかは、データベースへのリクエストを基準に計測されます。つまり、
- ダッシュボードを眺めるだけ
- アプリのURLを開くだけ
などでは活動しているとは見なされない場合があります。実際にDBへクエリが飛んで初めてタイマーがリセットされる、と理解しておくのが安全です。
有料のProプラン($25/月)にすると一時停止自体が無くなります。ですが今回は課金をせずに、無料枠のまま維持する方法を扱います。
今回の対策方法
今回はデモ用のアプリで、日常的に使っているわけでもなく、古いデータは特に必要なかったので、新しくプロジェクトを作って復旧しました。バックアップをダウンロードして新プロジェクトに復元する手もありますが、手間を考えると、
古いデータは捨てて、新規プロジェクトを作り直すのが一番速い
という判断をしました。データに価値がある場合は復元を検討すべきですが、「動くことが最優先/データは捨てても良い」なら作り直しが最短だと思います。
新しいSupabaseプロジェクトを作り、アプリの環境変数(NEXT_PUBLIC_SUPABASE_URL と NEXT_PUBLIC_SUPABASE_ANON_KEY)を差し替えて復旧完了。
これでプロジェクトを動かせるようになりましたが、このままだと放置した時にまたプロジェクトが止まってしまいます。そこで、DBに自動的にリクエストを飛ばし、一時停止を防ぐ方法を紹介します。
本題:GitHub Actionsで一時停止を防ぐ
仕組み
- GitHub Actionsが定期的に動く
- スクリプトがSupabaseのAPIを叩く
DBへリクエストが飛べば活動と見なされるため、一時停止を防ぐことができます。サーバーもDBも追加で用意する必要はなく、リポジトリにワークフローファイルを1つ置くだけで完結します。無料枠のまま運用できます。
手順1:Supabaseのキーを確認する
最初に、「Project URL」と「anon public key」を確認します。
-
Project URL(例:
https://your-project.supabase.co)
Supabaseダッシュボード → Project Settings → Data API -
anon public key
Supabaseダッシュボード → Project Settings → API Keys → Legacy anon, service_role API keys
手順2:GitHubにSecretsを登録する
リポジトリ → Settings → Secrets and variables → Actions → New repository secret で2つ登録します。
| Name | Secret |
|---|---|
SUPABASE_URL |
https://your-project.supabase.co |
SUPABASE_ANON_KEY |
(anon key) |
手順3:ワークフローファイルを作成する
リポジトリの .github/workflows/ ディレクトリにワークフローファイル(例:keep-supabase-alive.yml)を作ります。
name: Keep Supabase Alive
on:
schedule:
# 3日ごと、UTC 0時(日本時間 午前9時ごろ)に実行
- cron: '0 0 */3 * *'
workflow_dispatch: # 手動実行も可能にする
jobs:
ping:
runs-on: ubuntu-latest
steps:
- name: Ping Supabase Database
env:
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
run: |
curl -fsS -o /dev/null \
"$SUPABASE_URL/rest/v1/users?select=id&limit=1" \
-H "apikey: $SUPABASE_KEY" \
-H "Authorization: Bearer $SUPABASE_KEY"
users の部分は、自分のプロジェクトに実在するテーブル名なら何でも大丈夫です。
ワークフローの中身を1行ずつ読む
コピペで動きますが、意味がわかったほうが後で調整できます。上から順に見ていきましょう。YAMLはインデント(字下げ)で構造を表す言語で、スペースの数がずれると動かないので、そこだけ注意してください。
全体の骨組み
name: Keep Supabase Alive
on:
...
jobs:
...
GitHub Actionsのファイルは、だいたいこの3ブロックでできています。
-
name… このワークフローの名前。Actionsタブに表示される見出しです。好きな名前でOKです。 -
on… いつ動かすかを書く場所。 -
jobs… 何をするか(実際の作業)を書く場所。
「いつ」と「何を」の2つに分かれている、とだけ掴めれば十分です。
on:いつ動かすか
on:
schedule:
# 3日ごと、UTC 0時(日本時間 午前9時ごろ)に実行
- cron: '0 0 */3 * *'
workflow_dispatch: # 手動実行も可能にする
きっかけを2つ指定しています。
schedule(自動) は、決まった時刻に自動で動かす設定です。
cron: '0 0 */3 * *' の5つの値は、それぞれ「分 時 日 月 曜日」を表します。
| 値 | 位置 | 意味 |
|---|---|---|
0 |
分 | 0分に |
0 |
時 | 0時(UTC)に |
*/3 |
日 | 3日ごとに |
* |
月 | 毎月 |
* |
曜日 | どの曜日でも |
*(アスタリスク)は「すべて/指定なし」、*/3 は「3つおき」という意味です。合わせて「3日ごとの、UTC 0時0分に実行」になります。GitHub ActionsはUTC(世界標準時)で動くので、日本時間だと午前9時ごろです。
workflow_dispatch(手動) は、GitHubの画面に「Run workflow」ボタンを出して、自分の好きなタイミングで手動実行できるようにする設定です。動作確認に必須なのでつけておくと良いです。
jobs:何をするか
jobs:
ping:
runs-on: ubuntu-latest
steps:
- name: Ping Supabase Database
...
-
ping… 作業のまとまり(ジョブ)に付けた名前です。自分で好きに決められます。 -
runs-on: ubuntu-latest… このジョブをどのマシンで動かすかの指定です。GitHubが用意してくれるLinux(Ubuntu)の使い捨てPCを借りて実行する、という意味です。 -
steps… ジョブの中の手順のリストの始まりです。今回は一つだけです。 -
- name: ...… このステップの表示名です。Actionsのログ画面で見出しとして表示されます。
env:秘密の情報を安全に渡す
env:
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
URLやAPIキーをコードに直接書くと、リポジトリを見た人全員に知られてしまいます。そこでGitHubの Secrets(リポジトリの Settings → Secrets and variables → Actions で登録できる金庫のような場所)に値を保存しておき、${{ secrets.〇〇 }} という書き方で実行時に取り出します。
env: はそれをこのステップ内の環境変数($SUPABASE_URL のように $ 付きで呼び出せる箱)に入れる設定です。ここでは、手順2で SUPABASE_ANON_KEY という名前で登録したSecretを、スクリプト内では SUPABASE_KEY という短い名前で扱えるようにしています。Secretsに入れた値はログに出ても自動で *** にマスクされるので、キーが漏れる心配がありません。
心臓部:curlでSupabaseを叩く
run: |
curl -fsS -o /dev/null \
"$SUPABASE_URL/rest/v1/users?select=id&limit=1" \
-H "apikey: $SUPABASE_KEY" \
-H "Authorization: Bearer $SUPABASE_KEY"
run: | は「ここから下に書いたコマンドを実行する」という意味です。末尾の |(パイプ)はYAMLの複数行記法で、「この下は複数行のコマンドですよ」という印です。
curl は、コマンドラインからURLにアクセスするための道具です。ブラウザの代わりに、プログラムからSupabaseのAPIへリクエストを送っています。行末の \(バックスラッシュ)は「コマンドがまだ次の行に続く」という印で、長い1行を見やすく折り返しているだけです。
オプションを分解すると:
-
-f… HTTPエラー(404や500など)が返ってきたら、コマンド自体を失敗(非0の終了コード)にする。これが後述の「失敗検知」に繋がってきます。 -
-s… 途中経過(進捗バーなど)の余計な出力を消す。 -
-S…-sと組み合わせたとき、エラーメッセージだけは表示する。 -
-o /dev/null… 返ってきた中身は捨てる。(ログにデータを残さない) - 2行目のURL … Supabaseの
usersテーブルからidを1件だけ取ろうとしています(select=id&limit=1)。中身は要らなくて、「DBにアクセスがあった」という事実さえ作れれば一時停止を防げます。 -
-H "..."… リクエストに付けるヘッダー。SupabaseのREST APIはapikeyとAuthorization: Bearerの両方を要求するので、同じキーを2箇所に渡しています。
失敗はどうやって検知するのか
このワークフローには if 文がありませんが、ちゃんと失敗を検知できます。仕組みはこうです。
- Supabaseが正常なら、curlは成功(終了コード0)→ ステップが緑になる
- Supabaseが落ちている・URLが間違っているなどでHTTPエラーが返ると、
-fの効果でcurlが失敗(非0)→ ステップが赤い×になる
GitHub Actionsは「コマンドが非0で終わったらそのステップを失敗扱いにする」というルールで動いているので、-f を付けるだけで判定ロジックを自前で書かずに済みます。逆に -f がないと、Supabaseが死んでいてもジョブは常に緑のままで、延命できていないことに気づけません。
ワークフローのまとめ
全体の流れは、「3日ごとに、GitHubのUbuntuマシンがSupabaseへ問い合わせを投げ、届けば緑、エラーなら赤で知らせる」という仕組みです。
無料プランのSupabaseは一定期間アクセスがないとプロジェクトが一時停止されますが、このように定期的にAPIを叩くだけで「使われているプロジェクト」と見なされ、停止を防げます。
手順4:コミットして動作確認する
ファイルをpushしたら、GitHubの Actions タブ → Keep Supabase Alive → Run workflow で手動実行します。
今回のスクリプトは成功しても何も出力しない(-o /dev/null で中身を捨てている)ので、ステップに緑のチェック(✓)が付けば成功です。緑になっていること自体が「DBに到達できた」という印になります。
逆に赤い×が付いたら、pingが失敗しています。次のあたりを見直してください。
- SecretのURL・キーが正しいか(
SUPABASE_URL/SUPABASE_ANON_KEY) - YAML内の
secrets.〇〇の名前が、登録したSecret名と一致しているか - 叩いているテーブル名(
users)が実在し、anonキーでアクセスできるか
手動実行で緑になれば設定完了です。あとは3日ごとに自動で走ります。
まとめ
今回はバックエンドをSupabaseの無料枠で作成したプロジェクトを、GitHub Actionsで定期的にActivateする方法を紹介しました。初めてのハッカソンでSupabaseを使用する人も多いと思うので、ぜひこの方法を使ってみてください!
参考
- Project Pausing | Supabase Docs https://supabase.com/docs/guides/platform/free-project-pausing