Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
262 changes: 262 additions & 0 deletions client/www/app/docs/self-hosting/aws/page.md
Original file line number Diff line number Diff line change
@@ -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.<service-name>` 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.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
{% /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.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

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.
115 changes: 115 additions & 0 deletions client/www/app/docs/self-hosting/migrate/page.md
Original file line number Diff line number Diff line change
@@ -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.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

### 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! 🎉
Loading
Loading