HotupdaterHot Updater
Self Hosting (Custom)Database Adapters

S3

Since v0.22.0+

Build a self-hosted Hot Updater server with S3-compatible object storage.

Installation

Install the AWS plugin.

npm install @hot-updater/server @hot-updater/aws dotenv
npm install @hot-updater/bare @hot-updater/standalone hot-updater --save-dev

Setup

Create a .env.hotupdater file with your S3 credentials.

.env.hotupdater
S3_REGION=us-east-1
S3_ACCESS_KEY_ID=your-access-key
S3_SECRET_ACCESS_KEY=your-secret-key
S3_BUCKET_NAME=hot-updater
HOT_UPDATER_AUTH_TOKEN=replace-with-a-secret

For S3-compatible services like Cloudflare R2, MinIO, and DigitalOcean Spaces, set S3_ENDPOINT and use the region expected by that provider.

Hot Updater Configuration

Create the Hot Updater instance:

src/hotUpdater.ts
import { createHotUpdater } from "@hot-updater/server";
import { s3Database, s3Storage } from "@hot-updater/aws";
import { config } from "dotenv";

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

const s3Config = {
  region: process.env.S3_REGION ?? "auto",
  endpoint: process.env.S3_ENDPOINT,
  credentials: {
    accessKeyId: process.env.S3_ACCESS_KEY_ID!,
    secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
  },
  bucketName: process.env.S3_BUCKET_NAME!,
};

export const hotUpdater = createHotUpdater({
  database: s3Database(s3Config),
  storages: [s3Storage(s3Config)],
  basePath: "/hot-updater",
  routes: { updateCheck: true, bundles: true },
});

Protect /hot-updater/api/* with fail-closed authentication before mounting the management routes. See Security Settings.

Schema Management

No schema generation or migration command is required. s3Database stores bundle metadata as JSON objects in your S3 bucket.

Make sure the server credentials can list, read, write, and delete objects in the metadata bucket.

CLI Configuration

Configure your CLI to use this self-hosted server.

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

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

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: process.env.S3_REGION ?? "auto",
    endpoint: process.env.S3_ENDPOINT,
    credentials: {
      accessKeyId: process.env.S3_ACCESS_KEY_ID!,
      secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
    },
    bucketName: process.env.S3_BUCKET_NAME!,
  }),
  database: standaloneRepository({
    baseUrl: "http://localhost:3000/hot-updater",
    commonHeaders: { Authorization: `Bearer ${managementToken}` },
  }),
  updateStrategy: "appVersion",
});

The storage plugin must match the storages in your server's createHotUpdater. Both use s3Storage in this example.

API Endpoints

With the route groups enabled above, the server creates these endpoints:

MethodEndpointDescription
GET/hot-updater/versionGet server version
GET/hot-updater/fingerprint/:platform/:fingerprintHash/:channel/:minBundleId/:bundleIdCheck for updates by fingerprint
GET/hot-updater/app-version/:platform/:version/:channel/:minBundleId/:bundleIdCheck for updates by app version
GET/hot-updater/api/bundlesList bundles (query: channel, platform, limit, page, after, before)
GET/hot-updater/api/bundles/:idGet bundle by ID
POST/hot-updater/api/bundlesCreate bundles
PATCH/hot-updater/api/bundles/:idUpdate a bundle
DELETE/hot-updater/api/bundles/:idDelete bundle
GET/hot-updater/api/bundles/channelsList all channels

See the standalone server contract for the complete list-query encoding.

On this page