From 7fc1a9804c0e72d9d1ef182bb7a0eeebbd250f46 Mon Sep 17 00:00:00 2001 From: Joe Averbukh Date: Fri, 7 Aug 2026 11:39:30 -0700 Subject: [PATCH 1/2] Upgrade self host docs + migrate docs --- client/www/app/docs/self-hosting/aws/page.md | 262 ++++++++++++++ .../www/app/docs/self-hosting/migrate/page.md | 115 ++++++ client/www/app/docs/self-hosting/page.md | 341 +++++------------- client/www/app/docs/self-hosting/vps/page.md | 140 +++++++ client/www/data/docsNavigation.js | 13 +- 5 files changed, 626 insertions(+), 245 deletions(-) create mode 100644 client/www/app/docs/self-hosting/aws/page.md create mode 100644 client/www/app/docs/self-hosting/migrate/page.md create mode 100644 client/www/app/docs/self-hosting/vps/page.md diff --git a/client/www/app/docs/self-hosting/aws/page.md b/client/www/app/docs/self-hosting/aws/page.md new file mode 100644 index 0000000000..b9dbc93c0f --- /dev/null +++ b/client/www/app/docs/self-hosting/aws/page.md @@ -0,0 +1,262 @@ +--- +nextjs: + metadata: + title: 'Self Hosting Instant on AWS' + description: 'Run Instant with multiple backend servers and Aurora PostgreSQL.' +--- + +For more serious projects where you need higher availability and point in time +restores we recommend starting with two backend servers and +Aurora PostgreSQL. This is the same general architecture Instant Cloud used and +lets you scale the backend and database separately. + +The resources and instance types are up to you. The important parts are how the +Instant containers connect to PostgreSQL, object storage, and each other. + +## Architecture + +Instant does not require a particular AWS container platform. Your deployment +needs: + +- HTTPS traffic routed to every healthy backend +- At least two backend tasks for redundancy +- Private DNS resolving `tasks.` to all backend tasks +- Shared environment variables, secrets, and `override.edn` +- Aurora PostgreSQL 17 with logical replication +- Private S3 object storage +- A separately deployed dashboard + +Set `SWARM_SERVICE_NAME` to the service name used in private DNS. Backend tasks +must be able to communicate over ports 5701–5708 and 5801–5808. + +Do not set `PRODUCTION=true`; that selects Instant Cloud configuration rather +than self-hosted configuration. + +## Configure AWS access + +Before creating resources, choose an AWS CLI profile and Region, then confirm +that they point to the account where you intend to deploy: + +``` +aws sts get-caller-identity --profile your-profile +aws configure get region --profile your-profile +``` + +If AWS is not configured yet, use any AWS-supported authentication method to +create a CLI profile. An agent can help with this process. Use the selected +profile and Region consistently throughout the deployment. + +## Configure Aurora PostgreSQL + +Instant requires PostgreSQL 17 with logical replication and `pg_hint_plan`. +Create an Aurora PostgreSQL 17 cluster and apply these settings in a custom DB +cluster parameter group: + +```text +rds.logical_replication = 1 +shared_preload_libraries = pg_stat_statements,pg_hint_plan +max_replication_slots = 10 +max_wal_senders = 10 +random_page_cost = 1.1 +rds.force_ssl = 0 +``` + +Keep any existing entries in `shared_preload_libraries`. The replication +settings require a reboot. + +{% callout type="note" %} +Instant opens its migration connection without TLS, so Aurora PostgreSQL 17 +requires `rds.force_ssl = 0`. Restrict port 5432 to the backend servers. +{% /callout %} + +Create a database and login for Instant. The login needs the RDS replication +role: + +```sql +CREATE ROLE instant LOGIN PASSWORD 'replace-with-a-generated-password'; +GRANT rds_replication TO instant; +CREATE DATABASE instant OWNER instant; +``` + +Use the Aurora writer endpoint in `DATABASE_URL`: + +```shell +DATABASE_URL=postgresql://instant:PERCENT_ENCODED_PASSWORD@WRITER_ENDPOINT:5432/instant +``` + +Each backend opens `CONNECTION_POOL_SIZE` database connections. Make sure +Aurora's `max_connections` can support every backend instance with room for +migrations and administration. Increase `max_replication_slots` and +`max_wal_senders` if you run more than ten backend instances. + +## Configure S3 + +Create a private S3 bucket for Instant Storage. The backend needs permission to +list the bucket and to read, write, delete, and manage multipart uploads for its +objects. + +Set these values on every backend server: + +```shell +AWS_REGION=your-region +S3_BUCKET=instant-bucket +AWS_ACCESS_KEY_ID=replace-with-the-storage-access-key +AWS_SECRET_ACCESS_KEY=replace-with-the-storage-secret-key +``` + +Be sure to configure CORS on the bucket so Instant apps can upload files directly from the browser. + +Leave `S3_ENDPOINT` and `S3_PUBLIC_ENDPOINT` unset when using AWS S3. Instant +currently needs static IAM credentials to sign S3 URLs, so provide an access +key even when the application servers also have an instance role. + +## Share the encryption configuration + +Every backend instance must use the same `override.edn`. Instant uses this file +to encrypt secrets and sign webhooks. Generate it once: + +```sh {% showCopy=true %} +mkdir instant-config +docker run --rm \ + -v "$PWD/instant-config:/out" \ + ghcr.io/instantdb/server:latest \ + /app/start.sh generate-override-config /out/override.edn +``` + +Store it with your other deployment secrets and mount it at +`/app/resources/config/override.edn` on every backend. Do not generate a +different file for each server. + +## Deploy Instant + +### Run the backend servers + +Deploy the backend image on your preferred container platform: + +``` +ghcr.io/instantdb/server:latest +``` + +Start with two backend tasks in the same Availability Zone as the Aurora writer. +Route public backend traffic through the load balancer and use `/health/system` +for health checks. + +Every backend task must use the same environment variables and the same +`override.edn`. Mount it at: + +``` +/app/resources/config/override.edn +``` + +At minimum, configure: + +``` +WAL_HISTORY_STORAGE=pg +DATABASE_URL=postgresql://instant:PERCENT_ENCODED_PASSWORD@WRITER_ENDPOINT:5432/instant +CONNECTION_POOL_SIZE=20 + +INSTANT_BACKEND_URL=https://api.myinstant.com +INSTANT_DASHBOARD_URL=https://dash.myinstant.com + +AWS_REGION=your-region +S3_BUCKET=instant-bucket +AWS_ACCESS_KEY_ID=replace-with-the-storage-access-key +AWS_SECRET_ACCESS_KEY=replace-with-the-storage-secret-key + +JAVA_OPTS=-Xmx4g -Xms4g +``` + +Leave `S3_ENDPOINT` and `S3_PUBLIC_ENDPOINT` unset when using AWS S3. + +### Configure backend discovery + +Multiple backend tasks must form a single Hazelcast cluster for presence, topics, and distributed state to work correctly. + +Set a service name on every backend: + +``` +SWARM_SERVICE_NAME=server +``` + +Configure private DNS so that: + +``` +tasks.server +``` + +resolves to the private IP address of every backend task. Each task must be able +to reach the others over TCP ports 5701–5708 and 5801–5808. + +ECS with AWS Cloud Map, Docker Swarm DNSRR, or another scheduler that provides +equivalent private DNS can satisfy this requirement. + +Keep `PRODUCTION` unset. Setting `PRODUCTION=true` selects Instant Cloud's +production configuration rather than the mounted self-hosted `override.edn`. + +A successful `/health/system` response verifies the database WAL but does not +verify backend clustering. After deployment, test presence and realtime updates +while requests are distributed across both backend tasks. + +### Run the dashboard + +Deploy the dashboard image separately: + +``` +ghcr.io/instantdb/dashboard:latest +``` + +Set its public backend URL: + +``` +INSTANT_BACKEND_URL=https://api.myinstant.com +``` + +Route the dashboard hostname to port 3000: + +``` +https://dash.myinstant.com +``` + +The dashboard does not participate in backend service discovery. + +## Verify the deployment + +The load balancer should only send traffic to backends where +`/health/system` returns `{"wal":"ok"}`. Open the dashboard and create an app to +check queries, writes, realtime updates, and file uploads. + +Until Postmark is configured, login codes are written to the backend logs. For +an ECS deployment using CloudWatch Logs, tail the log group configured on the +backend task definition: + +```shell {% showCopy=true %} +aws logs tail /your/backend/log-group \ + --follow \ + --region your-region \ + --profile your-profile +``` + +Send application logs and infrastructure metrics wherever your team normally +monitors AWS services. At a minimum, watch request errors and latency, backend +health, server CPU and memory, Aurora connections and query latency, and S3 +errors. + +## Scale the deployment + +Add backend servers when CPU, memory, or request latency stays high. +Resize the Aurora writer when database CPU, memory, connections, or query +latency becomes the bottleneck. Revisit the connection pool and replication +settings whenever you add backend servers. + +Currently Instant Cloud runs on: + +| Tier | Capacity | +| ------------------- | ---------------------------------------------------------------------------------------------- | +| Application servers | 3 x `m6a.16xlarge` (64 vCPUs and 256 GiB each) | +| PostgreSQL | `db.r8gd.16xlarge` (64 vCPUs and 512 GiB) with Aurora I/O-Optimized | +| Workload | 10,000+ concurrent connections, 10,000+ queries per second, and 1,000+ transactions per second | + +Most deployments should start much smaller and scale each part from observed usage. + +Once Instant is running, see [Operating Instant](/docs/self-hosting#operating) +to configure email, dashboard access, the CLI, and health checks. diff --git a/client/www/app/docs/self-hosting/migrate/page.md b/client/www/app/docs/self-hosting/migrate/page.md new file mode 100644 index 0000000000..4aaf66bf81 --- /dev/null +++ b/client/www/app/docs/self-hosting/migrate/page.md @@ -0,0 +1,115 @@ +--- +nextjs: + metadata: + title: 'Migrate from Instant Cloud' + description: 'Move an Instant Cloud app to self-hosted Instant.' +--- + +Migrating from Instant Cloud happens in two phases: + +1. **Rehearse the migration:** Set up self-hosted Instant and restore a test backup. This confirms that everything works and gives you an estimate for downtime. +2. **Cut over:** Pause writes, restore a fresh backup, and point your app at your self-hosted Instant. + +## Rehearse the migration + +### Set up self-hosted Instant + +If you haven't already, set up self-hosting with our [VPS](/docs/self-hosting/vps) or +[AWS](/docs/self-hosting/aws) guide. Make sure you can log in to the dashboard, +create an app, query it, and write data. + +Before restoring your app: + +- [Configure Postmark](/docs/self-hosting#configure-email-with-postmark) so magic code emails work. +- [Restrict dashboard signups](/docs/self-hosting#restrict-dashboard-signups) and [disable temporary apps](/docs/self-hosting#temporary-apps) to prevent unwanted app creation. +- Similarly if your app uses webhooks you'll need to configure those for your + self-hosted app. + +If your app uses OAuth for end-user sign-in, recreate each OAuth provider on +the restored app. Copy its client ID, client secret, and any other provider +settings. Then add the self-hosted callback URL to the provider: + +```text +https://api.myinstant.com/runtime/oauth/callback +``` + +Keep the Instant Cloud callback configured until the migration is complete. + +### Restore a test backup + +Migrating without data loss will require some downtime. To get a sense of how +much time it will take we'll + +1. Export a backup from Instant Cloud +2. Restore the backup into your self hosted Instant. + +After restoring verify the following look correct: + +- Schema and permissions +- Application data +- Files +- Magic code and each OAuth provider your app uses +- Email templates + +### Prepare the client change + +After successfully restoring we can put up a PR to update our clients to point +to our new self-hosted Instant app. + +Choose a new app ID for the self-hosted app. The ID must be a valid UUID. You +can generate one in the terminal with: + +```sh {% showCopy=true %} +uuidgen +``` + +This will be the ID your app going forward. + +Create a PR that points your app at self-hosted Instant, but do not merge it +yet. Update the app ID, API URL, and WebSocket URL in every client `init` call: + +```ts +const db = init({ + appId: 'YOUR_NEW_APP_ID', + apiURI: 'https://api.myinstant.com', + websocketURI: 'wss://api.myinstant.com/runtime/session', +}); +``` + +If you use the Admin SDK, update its app ID, admin token, and `apiURI` too. Keep +the PR ready to merge as soon as the final restore finishes. + +## Cut over + +### Pause writes on Instant Cloud + +Open the app's **Admin** page in the Instant Cloud dashboard. Turn on +**Read-only mode**, then wait 30 seconds for in-flight mutations to finish. + +Reads, live queries, and presence will keep working. New writes will be +rejected, including offline writes queued on user devices. We do this to ensure +there is no data loss during cut over. + +### Restore the final backup + +Create an on demand backup of the Instant Cloud app and restore it into self-hosted +Instant using the app ID from the rehearsal. + +Before merging our earlier PR to switch clients over: + +- Check that `/health/system` returns `{"wal":"ok"}`. +- Check the restored schema, permissions, data, and files. +- Make sure **Read-only mode** is off on the self-hosted app. +- Test magic code and OAuth login if your app uses them. + +### Switch to self-hosted Instant + +Merge and deploy the PR you prepared earlier. New client connections will now +use the restored app on self-hosted Instant. Users may need to sign in again. + +Watch the deployment and verify queries, writes, authentication, and file +uploads. Once clients begin writing to self-hosted Instant, the Instant Cloud +copy is no longer current. If you used OAuth you can remove the Instant Cloud +callback URL from each OAuth provider. + +The migration should now be complete. Huzzah! 🎉 diff --git a/client/www/app/docs/self-hosting/page.md b/client/www/app/docs/self-hosting/page.md index 01b5134dd6..8bf680a482 100644 --- a/client/www/app/docs/self-hosting/page.md +++ b/client/www/app/docs/self-hosting/page.md @@ -1,114 +1,73 @@ --- nextjs: metadata: - title: 'Self Hosting' - description: 'Run InstantDB entirely on your own server.' + title: 'Self hosting' + description: 'Operate Instant on your own infrastructure.' --- -## Localhost Setup +Instant is fully open source! Below we have guides on how to locally hack +on Instant, set up self hosting, and migrate from Instant Cloud. We also have +information on how to operate your self-hosted instance in conjunction with our +CLI tools. -Use this setup if you want to self-host InstantDB while developing on your own machine. In most cases, it's more convenient to use our cloud service to spin up unlimited free apps. +## Hosting Guides -```shell {% showCopy=true %} -git clone https://github.com/instantdb/instant.git -cd instant/self-hosting -docker compose --env-file .env.example up -``` - -The dashboard will be available on [http://localhost:3000](http://localhost:3000). - -The server will be available on [http://localhost:8888](http://localhost:8888). +Choose a guide based on where you want to run Instant: -If you are developing an app and would like to use port 3000 (for example: NextJS), you can modify the port assignment in the `docker-compose.yml` file like so: +1. **Want to self host Instant for side projects?** Use our [VPS](/docs/self-hosting/vps) guide to self-host Instant starting at ~$30/mo. +1. **Want to self host Instant for your business?** If you're using Instant for more serious projects, check out our [AWS](/docs/self-hosting/aws) guide for a setup based on Instant Cloud. You should expect to spend at least $600/mo. +1. **Migrating from Instant Cloud?** Check out our [migration](/docs/self-hosting/migrate) guide. -```yaml -www: - ports: - - '3001:3000' -``` - -Then you will need to change the value for `INSTANT_DASHBOARD_URL` in `.env.example` +If you're an agent setting up self-hosting you should first ask the user whether +they are setting up self hosted Instant for side projects or for a +startup. For side projects the expected cost is at least $30/month. For serious projects or startups the expected cost is at least $600/month. Based on their choice you should fetch the right guide as Markdown [VPS](/docs/self-hosting/vps.md), [AWS](/docs/self-hosting/aws.md), [Migrate](/docs/self-hosting/migrate.md). -Apply changes with: +## Operating -```shell {% showCopy=true %} -docker compose --env-file .env.example up -d -``` +Once you've got Instant running you may want to change some default settings. At +the very least you should be sure to configure Postmark so magic code emails can +actually deliver. -## Full Hetzner Setup Guide +### Configure email with Postmark -Create a new server on Hetzner. We tested on a server with 4 vCPU, 8 GB RAM. This guide also shows how to set up TLS. For memory-constrained environments, set the `JAVA_OPTIONS` environment variable to limit the memory the server container uses. +**Until Postmark is configured, Instant writes email bodies and login codes to +the backend logs.** -This setup guide assumes that you have a domain name and can set DNS A records. -{% callout type="note" %} +Instant comes with support for auth and sending magic code emails. The easiest way to actually send emails to create a [Postmark server](https://postmarkapp.com/), verify the sender addresses, and set: -If you do not have a domain name, you can use [sslip.io](https://sslip.io/) to create a domain name on the fly that points to the IP address of your server. - -{% /callout %} - -### Install Docker (optional if docker is already installed) - -The following Docker install instructions come from [docs.docker.com](https://docs.docker.com/engine/install/ubuntu/) - -```sh {% showCopy=true %} -apt update -sudo apt install ca-certificates curl -sudo install -m 0755 -d /etc/apt/keyrings -sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc -sudo chmod a+r /etc/apt/keyrings/docker.asc -``` - -```sh {% showCopy=true %} -sudo tee /etc/apt/sources.list.d/docker.sources < -``` - -For each machine that you would like to accept requests, make sure you assign the "caddy=true" output and point your DNS at that IP address. - -Choose one machine that is responsible for file storage and assign the "storage=true" label. This ensures that the Postgres and MinIO containers will always run on that node, preserving your data. +### View health -The 'www', 'createbuckets', and 'server' containers can move freely between nodes since they don't have persistent storage requirements. +Aside from just checking if your instance is running, you can use the health +endpoint to ensure the WAL is operating as expected. -### Creating The Encryption Keys - -The InstantDB backend server requires the presence of a file at /app/resources/config/override.edn to provide keys for encrypting various secrets in the backend. In the single-container self-hosted setup, this is accomplished via a startup script that creates a file persisted to a volume. Since the backend service will be running on multiple machines without shared storage, we instead use [Docker Secrets](https://docs.docker.com/engine/swarm/secrets/) to mount this file. - -The easiest way to register this secret is to run the following on a leader machine. - -```bash -docker run --rm \ - -v "$PWD:/out" \ - ghcr.io/instantdb/server:latest \ - /app/start.sh generate-override-config /out/override.edn - -docker secret create server_config ./override.edn +```shell +curl -fsS https://api.myinstant.com/health/system ``` -### Configuring Environment Variables +A healthy backend returns `{"wal":"ok"}`. Alert on non-200 responses and any +other body. -Rather than cloning the repo on a server and filling out a .env file, we have provided a script that you can modify and execute to easily deploy the services with the proper configuration. +### Horizontal scaling -To get started: clone the repo on your local computer and edit the `./self-hosting/deploy-swarm.sh` file. +A multi-server Instant deployment requires more than increasing the number of containers. Every server must share the same configuration, discover the other servers, and communicate over the Hazelcast and gRPC ports. Docker Swarm provides this through its built-in service discovery. Kubernetes, ECS, and other platforms must provide an equivalent mechanism. -```bash -SSH_HOST=root@ip -DASHBOARD_URL="https://dashboard.example.com" -SERVER_URL="https://backend.example.com" -S3_PUBLIC_ENDPOINT="https://files.example.com" -INSTANT_DASHBOARD_GOOGLE_OAUTH_CLIENT_ID="your-client-id.apps.googleusercontent.com" -INSTANT_DASHBOARD_GOOGLE_OAUTH_CLIENT_SECRET="your-client-secret" +### Memory Limits -stack_config() { - env -i \ - PATH="$PATH" \ - DASHBOARD_URL="$DASHBOARD_URL" \ - SERVER_URL="$SERVER_URL" \ - S3_PUBLIC_ENDPOINT="$S3_PUBLIC_ENDPOINT" \ - INSTANT_DASHBOARD_GOOGLE_OAUTH_CLIENT_ID="$INSTANT_DASHBOARD_GOOGLE_OAUTH_CLIENT_ID" \ - INSTANT_DASHBOARD_GOOGLE_OAUTH_CLIENT_SECRET="$INSTANT_DASHBOARD_GOOGLE_OAUTH_CLIENT_SECRET" \ - docker stack config --compose-file swarm.yml -} +By default, the backend server container can use a lot of resources. Set a +maximum heap size with `JAVA_OPTS` and leave memory for the operating system and +other containers. On the 4 GB VPS from this guide, start with a 2 GB heap: -stack_config | (ssh $SSH_HOST 'docker stack deploy -c - instant') +```yaml {%lineHighlight="3"%} +server: + environment: + JAVA_OPTS: -Xmx2g -Xms2g ``` -Docker Swarm/Stack doesn't support loading environment variables from .env files so we use the `docker stack config` command to fill out a completed yaml specification and send that to the `docker stack deploy` command on the swarm's manager node by using stdin over ssh. - -`SSH_HOST` is the username and IP address of the _manager_ node to the swarm you are deploying to. - -For `DASHBOARD_URL`, `SERVER_URL`, and `S3_PUBLIC_ENDPOINT`, modify the domain/subdomains to match what you assigned in the DNS for your domain. - -To configure other environment variables such as a Postmark token to send emails, add another row in the env -i command. Adding the variables outside of stack_config() will not work in order to prevent accidental leakage of environment variables not meant for the deployment. +This sets both the minimum and maximum JVM heap to 2 GB. Larger, dedicated +backend containers can use a larger heap, but should still leave memory outside +the JVM. diff --git a/client/www/app/docs/self-hosting/vps/page.md b/client/www/app/docs/self-hosting/vps/page.md new file mode 100644 index 0000000000..2ba374cda2 --- /dev/null +++ b/client/www/app/docs/self-hosting/vps/page.md @@ -0,0 +1,140 @@ +--- +nextjs: + metadata: + title: 'Self hosting on VPS' + description: 'Run Instant on a VPS.' +--- + +You can run the Instant backend, dashboard, PostgreSQL, MinIO, and Caddy on a +single VPS. A server with 2 vCPUs and 4 GB of RAM is enough to get started and +usually costs around $30 per month. + +Already have a VPS and domain? Continue with the server's SSH address and the +hostnames you want to use. + +Starting from scratch? Ask your agent for guided help creating the VPS and +setting up DNS. + +Any provider that offers a recent Ubuntu image will work. We've tested this +setup with [DigitalOcean](https://www.digitalocean.com/pricing/droplets/) and +[Hetzner](https://www.hetzner.com/cloud/). + +## Create the server + +Start with: + +- Ubuntu 24.04 LTS +- 2 vCPUs +- 4 GB of RAM + +You will also need a domain where you can create DNS records. This guide uses: + +```text +dash.myinstant.com +api.myinstant.com +files.myinstant.com +``` + +Point each hostname to the server. + +Allow inbound HTTP, HTTPS, and administrator access to the server. PostgreSQL +and MinIO only need to be reachable by the other containers on the host. + +## Install Docker + +Skip this section if Docker and the Compose plugin are already installed. These +commands come from Docker's [Ubuntu installation guide](https://docs.docker.com/engine/install/ubuntu/): + +```sh {% showCopy=true %} +sudo apt update +sudo apt install -y ca-certificates curl git +sudo install -m 0755 -d /etc/apt/keyrings +sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc +sudo chmod a+r /etc/apt/keyrings/docker.asc +``` + +```sh {% showCopy=true %} +sudo tee /etc/apt/sources.list.d/docker.sources < Date: Fri, 7 Aug 2026 11:42:24 -0700 Subject: [PATCH 2/2] sm --- client/www/app/docs/self-hosting/page.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/www/app/docs/self-hosting/page.md b/client/www/app/docs/self-hosting/page.md index 8bf680a482..46ea35eea9 100644 --- a/client/www/app/docs/self-hosting/page.md +++ b/client/www/app/docs/self-hosting/page.md @@ -5,10 +5,8 @@ nextjs: description: 'Operate Instant on your own infrastructure.' --- -Instant is fully open source! Below we have guides on how to locally hack -on Instant, set up self hosting, and migrate from Instant Cloud. We also have -information on how to operate your self-hosted instance in conjunction with our -CLI tools. +Instant is fully open source! Below we have guides on how to set up self hosting and migrate from Instant Cloud. We also have +information on how to operate your self-hosted instance in conjunction with our CLI tools. ## Hosting Guides