2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

一人で1年運用したサービスの構成と月額コスト全公開 ─ DAU 1000人で月額480円に収めた設計

2
Posted at

1年前にリリースした個人サービスが、ありがたいことに DAU 1000人前後で安定運用できている。驚くのは、このサービスの月額インフラコストが 480円 だということだ。本稿ではその構成と、どこにいくら払っているかを全部公開する。数字は執筆時点で実際に請求されている額だ。

サービス概要

  • ジャンル: エンジニア向け情報集約ツール
  • DAU: 約 1000
  • MAU: 約 8000
  • 月間PV: 約 45万
  • 技術スタック: Next.js + Cloudflare + Supabase

月額コスト内訳

項目 サービス 金額
独自ドメイン お名前.com 月 120円 (年1430円 / 12)
DNS Cloudflare 0円
フロント Cloudflare Pages 0円
API Cloudflare Workers Paid 月 360円 ($5 / 月を日本円換算)
DB Supabase Free 0円
画像配信 Cloudflare R2 0円 (無料枠内)
メール Resend Free 0円
分析 Plausible (self-host) Workers と同居 / 実質0円
合計 月 480円

構成図

[User]
  |
  v
[Cloudflare DNS] --> [Cloudflare Pages]
                         |
                         v
                   [Cloudflare Workers (Hono)]
                         |
         +---------------+---------------+
         v               v               v
    [Supabase DB]    [R2 (画像)]     [Resend (通知)]

なぜこの構成にしたか

Pages + Workers の分離

最初はすべて Next.js の Vercel デプロイで動かしていたが、帯域上限が怖かったので Pages へ寄せた。ISR が必要な部分だけ Workers で返す構成にしている。

Supabase Free のまま

DB サイズは執筆時点で 230MB、Auth は MAU 8000 なので無料枠の範囲内。バックアップだけは自前で GitHub Actions で日次取得している。

# .github/workflows/backup.yml
name: daily-backup
on:
  schedule:
    - cron: '15 18 * * *' # UTC 18:15 = JST 03:15
jobs:
  backup:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Dump DB
        env:
          PGURL: ${{ secrets.SUPABASE_DB_URL }}
        run: |
          sudo apt-get install -y postgresql-client
          pg_dump "$PGURL" > dump-$(date +%Y%m%d).sql
      - name: Upload to R2
        env:
          R2_KEY: ${{ secrets.R2_ACCESS_KEY }}
          R2_SECRET: ${{ secrets.R2_SECRET }}
        run: |
          # aws-cli で R2 にアップロード
          aws s3 cp dump-*.sql s3://my-backup/ --endpoint-url $R2_ENDPOINT

画像は R2 に置く

Supabase Storage は無料枠 1GB とやや狭いので、ユーザー投稿画像は R2 に逃がしてある。R2 はエグレス無料なので、いくら配信されても追加料金が発生しない。

API の中身

import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { rateLimiter } from 'hono/rate-limiter'

const app = new Hono()

app.use('*', cors({ origin: ['https://example.com'] }))
app.use('*', rateLimiter({ windowMs: 60_000, limit: 60 }))

app.get('/feed', async (c) => {
  // キャッシュにヒットすれば DB を叩かない
  const cached = await c.env.CACHE.get('feed', 'json')
  if (cached) return c.json(cached)
  const res = await fetchFromSupabase(c.env)
  await c.env.CACHE.put('feed', JSON.stringify(res), { expirationTtl: 120 })
  return c.json(res)
})

export default app

KV 2分キャッシュを挟んだだけで、Workers のリクエスト消費が半減した。

月次の観察

  • Workers リクエスト: 月 450万
  • D1/Supabase 読み取り: 月 200万
  • R2 Class A: 月 8万
  • R2 ストレージ: 執筆時点で 2.3GB

Workers の月 450万リクエストは Paid プランの範囲内に収まる。

コスト試算と将来予測

DAU 5000 までなら、この構成のまま月額 480円で行ける見積もりだ。DAU 1万を超えたあたりで Supabase を Pro (月25ドル) に上げる必要が出てくる。逆に言えば、5倍スケールするまで構成を変えなくてよい

まとめ

  • 個人開発は500円以下で DAU 1000を回せる
  • 画像配信は R2 一択
  • キャッシュ層を入れるだけで DB とAPIの負荷は劇的に下がる

「サーバー代が怖いから個人開発に踏み出せない」というのは、2026年の現実には合わない心配だ。

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?