# Vercel storage implementation guide: choosing Blob, Edge Config, and Marketplace (Neon/Upstash) correctly by use

> A storage-selection and implementation guide faithful to Vercel's official docs. With real code, it explains: Vercel Blob (public/private, @vercel/blob's put/del/list, client/server upload, conditional writes ifMatch, multipart, S3 backend 11 nines), Edge Config (P99 sub-15ms global reads, feature flags/redirects/IP block), and Marketplace (Neon Postgres, Upstash Redis; Vercel Postgres/KV discontinued).

- Published: 2026-06-28
- Author: 友田 陽大
- Tags: Vercel, ストレージ, Next.js, TypeScript, コスト最適化, アーキテクチャ設計, PostgreSQL
- URL: https://tomodahinata.com/en/blog/vercel-storage-blob-edge-config-marketplace-guide
- Category: Vercel in production
- Pillar guide: https://tomodahinata.com/en/blog/vercel-production-platform-guide

## Key points

- Vercel Postgres / Vercel KV are discontinued. Now you choose by use — objects in Vercel Blob, ultra-low-latency reads in Edge Config, a relational DB in Marketplace's Neon (Postgres), and KV/Redis likewise in Upstash.
- Vercel Blob is public/private (unchangeable after creation). Operate with @vercel/blob's put/del/list/head/copy. For large files or user uploads, use client upload to avoid data-transfer billing and bypass the server. The S3 backend gives 11 nines durability, 4 nines availability, 20 regions.
- Blob has addRandomSuffix to prevent collisions, allowOverwrite to overwrite, and ifMatch for optimistic concurrency control (succeeds only on ETag match, BlobPreconditionFailedError). multipart is recommended over 100MB. Treating it as immutable (create with a new pathname) is the safe basis.
- Edge Config is a global read-only store with P99 sub-15ms (often sub-1ms). It's ideal for 'high read, low write' like feature flags, emergency redirects, and IP blocks. Reads are protected with a read token, writes with an API token.
- Marketplace integrations connect with vercel integration, auto-injecting environment variables and unifying billing. Serverless Postgres needs care for connection exhaustion, so use connection pooling (Neon's pooled connection / PgBouncer-equivalent).

---

"Where to put data on Vercel" changed greatly in 2026. The former **Vercel Postgres / Vercel KV are discontinued**, and now it's a design of **choosing the optimal storage per use.** Get the selection wrong and you'll suffer on cost, latency, or connection exhaustion. This article, faithful to the official specs of [Vercel Blob](https://vercel.com/docs/vercel-blob), [Edge Config](https://vercel.com/docs/edge-config), and [Marketplace](https://vercel.com/docs/integrations), collects how to choose and use them in real code.

For the big picture, see the [Vercel production-operations guide](/blog/vercel-production-platform-guide).

---

## Choose by use: four options

| What you want to store | Option | In one line |
|---|---|---|
| **Objects** (images, video, PDFs, large files) | **Vercel Blob** | S3-backed object storage. CDN delivery |
| **Small data read with ultra-low latency** (flags, redirects, IP lists) | **Edge Config** | Global reads with P99 sub-15ms |
| **Relational DB** | **Marketplace: Neon (Postgres)** | Serverless Postgres. Auto env injection |
| **KV / cache / rate limiting** | **Marketplace: Upstash (Redis)** | Serverless Redis |

Decision shortcuts:

- Files users upload → **Blob (client upload)**
- A **high-read, low-write** setting like "maintenance mode ON/OFF" → **Edge Config**
- Core data needing transactions/relations/aggregation → **Neon (Postgres)**
- Sessions, cache, distributed rate limiting → **Upstash (Redis)**

---

## Vercel Blob: object storage

### public and private (unchangeable after creation)

A Blob store **chooses public or private at creation and can't change it later.** The delivery path changes by access mode ([Blob docs](https://vercel.com/docs/vercel-blob)).

| | Private | Public |
|---|---|---|
| Write | Auth required | Auth required |
| Read | Auth required (token needed) | Anyone who knows the URL |
| Delivery | **Via a function** (`get()`) | Direct Blob URL |
| Suited for | Confidential documents, user content | Large media, public assets |

> **Don't get the choice wrong**: confidential things like invoice PDFs and identity documents are **private**, no question. Avatars and public images are **public.** Since you can't change it after creation, split stores per use.

### Basic operations (@vercel/blob)

```ts
import { put, del, list, head, copy } from "@vercel/blob";

// アップロード（put）
const blob = await put(`invoices/${id}.pdf`, pdfBuffer, {
  access: "private",        // 'public' も
  addRandomSuffix: true,    // 'invoices/abc-<random>.pdf' 衝突防止
  contentType: "application/pdf",
});

// メタデータ取得（head）— ETag も取れる
const meta = await head(blob.url);

// 一覧（list）— prefix でフォルダ的に絞る
const { blobs } = await list({ prefix: "invoices/", limit: 1000 });

// 削除（del）— 課金対象外（無料）
await del(blob.url);
```

### User uploads go via client upload

If you **receive a user-uploaded file at the server (function) once and transfer it to Blob**, the function's data-transfer billing applies, and you hit the 4.5MB request-body limit. With **client upload**, you can send **directly to Blob** from the browser, with no data-transfer billing either.

```ts
// サーバー：アップロード用トークンを発行（認可をここで行う）
// app/api/upload/route.ts
import { handleUpload, type HandleUploadBody } from "@vercel/blob/client";

export async function POST(request: Request): Promise<Response> {
  const body = (await request.json()) as HandleUploadBody;
  const json = await handleUpload({
    body,
    request,
    onBeforeGenerateToken: async (pathname) => {
      // ★ ここで認証・拡張子・サイズ制限などの認可を行う
      await assertAuthenticated(request);
      return {
        allowedContentTypes: ["image/jpeg", "image/png", "application/pdf"],
        addRandomSuffix: true,
      };
    },
    onUploadCompleted: async ({ blob }) => {
      // アップロード完了後の後処理（DBへURL保存など）
      await saveBlobReference(blob.url);
    },
  });
  return Response.json(json);
}
```

```tsx
// クライアント：ブラウザから直接 Blob へ（サーバーを経由しない）
"use client";
import { upload } from "@vercel/blob/client";

async function onFile(file: File) {
  const blob = await upload(file.name, file, {
    access: "public",
    handleUploadUrl: "/api/upload", // ↑のトークン発行エンドポイント
  });
  return blob.url;
}
```

For files over 100MB, **multipart upload** is recommended (`put`/`upload` auto-handle splitting, parallelism, and resume; even on a network drop, only the relevant part is resent).

### Concurrent updates safely: conditional writes (ifMatch)

When multiple processes might update the same Blob (a shared config file, etc.), use **optimistic concurrency control.** Pass the ETag obtained from `head()`/`get()` to `ifMatch`, and it succeeds **only if it hasn't changed since.**

```ts
import { head, put, BlobPreconditionFailedError } from "@vercel/blob";

const meta = await head("config.json");
try {
  await put("config.json", JSON.stringify(next), {
    access: "private",
    allowOverwrite: true,
    ifMatch: meta.etag, // 他プロセスが更新していたら失敗
  });
} catch (e) {
  if (e instanceof BlobPreconditionFailedError) {
    // 競合：再読込してリトライ or 衝突処理
  }
  throw e;
}
```

### Caching and immutable operation

Blob is cached on the CDN for **1 month by default** (changeable with `cacheControlMaxAge`). Reflecting an update/delete takes **up to 60 seconds**, and the browser cache is separate. So the official recommendation is **"treat Blob as immutable"** — instead of updating, **create with a new pathname** (`addRandomSuffix` or timestamp/UUID). Only for data you really must update at the same URL (a ranking JSON every 5 minutes, etc.), overwrite with a short `cacheControlMaxAge`.

Durability is **99.999999999% (11 nines)** on the S3 backend, and availability **99.99% (4 nines).** You can safely use it as a production file foundation.

---

## Edge Config: ultra-low-latency global reads

### What's good about it

Edge Config is a global store for "**fast reads, rare writes**" data. At P99 **sub-15ms** (often **sub-1ms**), it reads near the user without hitting an external DB ([Edge Config docs](https://vercel.com/docs/edge-config)).

Representative uses:

- **Feature flags / A-B tests**: ON/OFF without redeploying
- **Emergency redirects / maintenance mode**: on an incident, redirect instantly without touching code
- **IP blocklist**: reject malicious IPs without calling the upstream

### Reading (SDK)

```ts
// Middleware や関数で（@vercel/edge-config）
import { get, getAll, has } from "@vercel/edge-config";

// 機能フラグ
const newCheckout = await get<boolean>("feature_new_checkout");

// メンテモード（Routing Middleware で全リクエストに対し超低遅延で判定）
export async function middleware(request: Request) {
  if (await get<boolean>("maintenance_mode")) {
    return Response.redirect(new URL("/maintenance", request.url));
  }
}
```

### Writing and protecting

- **Reads** are protected with a read access token, **writes** with an API token (**secure by default**).
- Writes are designed to happen **outside the request path** (the dashboard or REST API). In other words, it's not something you "write during the app's response."
- Vercel's read optimization works on the **Edge / Node.js runtime** (others require an application).

> **Not a substitute for a DB**: Edge Config is for "small, rarely updated settings." Send user data and frequently written data to Neon/Upstash. The difference from environment variables is that **it can be updated without a redeploy, with no downtime risk.**

---

## Marketplace: integrating Neon and Upstash

Integrate a relational DB or Redis from the **Vercel Marketplace.** Connecting with `vercel integration` **auto-injects environment variables like the connection string** and **unifies billing to Vercel.**

```bash
# Marketplace 統合の接続（例）
vercel integration add neon       # サーバーレス Postgres
vercel integration add upstash    # サーバーレス Redis
vercel env pull .env.local        # 注入された接続情報をローカルへ
```

### Mind connection exhaustion in serverless

Postgres's "1 connection = 1 process" is heavy, and under **Fluid Compute's concurrency** connections tend to be exhausted. The countermeasure is **connection pooling** — use Neon's **pooled connection string** (internally PgBouncer-equivalent) and connect to the pooled endpoint from serverless.

```ts
// Neon：pooled 接続を使う（サーバーレスでは必須級）
import { neon } from "@neondatabase/serverless";
const sql = neon(process.env.DATABASE_URL!); // pooled エンドポイント

export async function GET() {
  const rows = await sql`SELECT id, title FROM posts ORDER BY created_at DESC LIMIT 20`;
  return Response.json(rows);
}
```

The design principles of connection pooling (why it's mandatory in serverless, the transaction-mode constraints) are detailed in the [PostgreSQL connection-pooling / PgBouncer guide](/blog/postgresql-connection-pooling-pgbouncer-serverless-guide). To put a type-safe ORM over it, see [Drizzle ORM](/blog/drizzle-orm-typescript-type-safe-database-production-guide) or [Prisma v7](/blog/prisma-orm-production-guide-type-safe-database-v7-driver-adapters), and for distributed rate limiting with Redis, [serverless rate limiting](/blog/nextjs-serverless-rate-limiting-vercel-guide).

---

## Production checklist (storage)

- [ ] **Choose the storage location** by the nature of the data (object/config/relational/KV)
- [ ] **Settle** Blob public/private by use (unchangeable after creation)
- [ ] User uploads via **client upload** (avoid transfer billing and the 4.5MB limit), with **authorization** at token issuance
- [ ] **Immutable operation** for Blob (`addRandomSuffix`), `ifMatch` for concurrent updates
- [ ] Confidential files are **private** + delivered via a function
- [ ] Feature flags and maintenance mode in **Edge Config** (no redeploy)
- [ ] Postgres prevents connection exhaustion with **pooled connections**
- [ ] Marketplace integration's **environment variables are auto-injected**; don't write secrets in code

---

## Conclusion

2026's Vercel storage isn't "everything in one DB" but a design of **combining the optimal solution per use.**

1. **Objects in Blob** (client upload, immutable, private/public correctly)
2. **High-read settings in Edge Config** (ultra-low latency, no redeploy)
3. **Relational in Neon, KV/Redis in Upstash** (Marketplace integration)
4. In serverless, **connection pooling** to prevent exhaustion
5. Secrets in environment variables, Blob uploads with **authorization at token issuance**

Next, go to the [cost / Active CPU optimization guide](/blog/vercel-cost-active-cpu-pricing-optimization-guide) for operating these **cheaply.**

> This article is based on the official documentation of [Vercel Blob](https://vercel.com/docs/vercel-blob) / [Edge Config](https://vercel.com/docs/edge-config) / [Marketplace](https://vercel.com/docs/integrations) (as of June 2026). The spec, limits, and pricing get updated, so confirm the latest values in the official docs when adopting in production.
