HotupdaterHot Updater
Self Hosting (Custom)Hosting

Vercel

Since v0.22.0+

Deploy Hot Updater server to Vercel with serverless functions.

Prerequisites

  • Vercel account at vercel.com
  • A current Marketplace Postgres integration, such as Neon
  • Cloud storage (S3, R2, Supabase, Firebase, or custom)

Installation

Install required dependencies.

npm install @hot-updater/server @hot-updater/aws hono drizzle-orm pg
npm install @hot-updater/bare @hot-updater/standalone hot-updater drizzle-kit @types/pg dotenv --save-dev

Connect Postgres

Install a Postgres integration from the Vercel Marketplace, link the project, and pull its environment variables for local schema work:

npx vercel link
npx vercel env pull .env.local

Add the Hot Updater management and storage values to .env.local for local development, and configure the same names for the Production environment in your Vercel project settings:

.env.local
HOT_UPDATER_AUTH_TOKEN=replace-with-a-secret
R2_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com
R2_ACCESS_KEY_ID=your-access-key-id
R2_SECRET_ACCESS_KEY=your-secret-access-key
R2_BUCKET_NAME=your-bucket-name

Database Setup

Create the database connection file.

src/drizzle.ts
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import * as schema from "../hot-updater-schema";

const pool = new Pool({ connectionString: process.env.DATABASE_URL! });
export const db = drizzle({ client: pool, schema, casing: "snake_case" });
drizzle.config.ts
import { config } from "dotenv";
import { defineConfig } from "drizzle-kit";

config({ path: ".env.local" });

export default defineConfig({
  schema: "./hot-updater-schema.ts",
  out: "./drizzle",
  dialect: "postgresql",
  dbCredentials: { url: process.env.DATABASE_URL! },
});

Hot Updater Configuration

Create the Hot Updater instance.

src/hotUpdater.ts
import { createHotUpdater } from "@hot-updater/server";
import { drizzleAdapter } from "@hot-updater/server/adapters/drizzle";
import { s3Storage } from "@hot-updater/aws";
import { db } from "./drizzle";

export const hotUpdater = createHotUpdater({
  database: drizzleAdapter({ db, provider: "postgresql" }),
  storages: [
    s3Storage({
      region: "auto",
      endpoint: process.env.R2_ENDPOINT!,
      credentials: {
        accessKeyId: process.env.R2_ACCESS_KEY_ID!,
        secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
      },
      bucketName: process.env.R2_BUCKET_NAME!,
    }),
  ],
  basePath: "/hot-updater",
  routes: { updateCheck: true, bundles: true },
});

Serverless Function

Create Vercel serverless function with Hono.

src/index.ts
import { Hono } from "hono";
import { bearerAuth } from "hono/bearer-auth";
import { hotUpdater } from "./hotUpdater";

const managementToken = process.env.HOT_UPDATER_AUTH_TOKEN;
if (!managementToken) {
  throw new Error("HOT_UPDATER_AUTH_TOKEN is required");
}

const app = new Hono();

app.use("/hot-updater/api/*", bearerAuth({ token: managementToken }));
app.mount("/hot-updater", hotUpdater.handler);

export default app;

With Hono mount(), update-check routes such as /hot-updater/app-version/* and /hot-updater/fingerprint/* are usually exposed for React Native clients, but that is a deployment choice. /hot-updater/version is always mounted for diagnostics. Always protect /hot-updater/api/* when routes.bundles: true.

Schema Generation

Generate the Drizzle schema for Hot Updater tables.

npx hot-updater db generate src/hotUpdater.ts --yes
npx drizzle-kit push

db generate writes hot-updater-schema.ts; drizzle-kit push applies it to Postgres. Use reviewed migrations for production change control.

Development

Run the development server locally.

npx vercel dev

Server runs at http://localhost:3000.

Deploy

Deploy to Vercel.

npx vercel deploy --prod

Your Hot Updater server will be available at https://your-project.vercel.app/hot-updater.

CLI Configuration

Configure your CLI to use this deployed server.

hot-updater.config.ts
import { bare } from "@hot-updater/bare";
import { s3Storage } from "@hot-updater/aws";
import { standaloneRepository } from "@hot-updater/standalone";
import { defineConfig } from "hot-updater";

const managementToken = process.env.HOT_UPDATER_AUTH_TOKEN;
if (!managementToken) {
  throw new Error("HOT_UPDATER_AUTH_TOKEN is required");
}

export default defineConfig({
  build: bare({ enableHermes: true }),
  storage: s3Storage({
    region: "auto",
    endpoint: process.env.R2_ENDPOINT!,
    credentials: {
      accessKeyId: process.env.R2_ACCESS_KEY_ID!,
      secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
    },
    bucketName: process.env.R2_BUCKET_NAME!,
  }),
  database: standaloneRepository({
    baseUrl: "https://your-project.vercel.app/hot-updater",
    commonHeaders: { Authorization: `Bearer ${managementToken}` },
  }),
  updateStrategy: "appVersion",
});

The storage plugin must match the storages in your server's createHotUpdater.

On this page