Skip to main content
Use this guide for the first customer-controlled deployment. It proves that the release can start in the customer’s VPC, reach PostgreSQL and Redis, complete OIDC sign-in, persist a conversation, and recover after a container restart.
This is a disposable sanity check, not the pilot production architecture. It runs one web container, one worker, and one scheduler on one EC2 host. Move to Deploy On AWS after this check passes.

What this runs

The sanity profile deliberately disables files, memory, knowledge, vector search, automations, webhooks, integrations, browser use, sandboxes, analytics, error reporting, and billing. Enable those only in the qualified AWS deployment.

Before you start

You need: Use security-group references, not public database endpoints:
  • EC2 inbound: SSH only from the operator’s current IP. Do not expose port 3000.
  • RDS inbound: PostgreSQL 5432 only from the EC2 security group.
  • Redis inbound: TLS Redis 6379 only from the EC2 security group.
  • EC2 outbound: HTTPS plus the approved RDS and Redis destinations.

Install

1

Connect to the EC2 instance

Connect through Session Manager when available. Otherwise use the temporary, IP-restricted SSH rule:
2

Install Docker, Git, and PostgreSQL tools

Review the Docker installation script before running it if customer policy requires source inspection.
Install the AWS RDS CA bundle for administrative psql commands:
3

Create the two databases with the RDS administrator

Run this once with a temporary administrative connection. The application and Better Auth roles must be separate and must not retain administrative membership. Replace all angle-bracket values before running it.
RDS requires its administrative role to install pgvector, even when the narrow sanity profile keeps vector search disabled. The normal migration and runtime commands must use the restricted overlay_app role afterward. If roles or databases already exist, inspect them instead of blindly rerunning the creation statements.
4

Clone the approved release

Use the customer-controlled repository when one exists.
Record the exact commit:
5

Create the runtime files

The real environment file is ignored by Git.
Edit deploy/ec2/.env and replace every replace_me or replace-me value. Generate every Overlay-owned secret independently:
Do not reuse a generated value. Do not paste secret values into chat, tickets, logs, screenshots, Git, or the coding agent’s final report.Database passwords containing reserved URL characters must be URL-encoded in OVERLAY_DATABASE_URL and BETTER_AUTH_DATABASE_URL.
6

Configure Google Workspace login

In a customer-controlled Google Cloud project, configure Google Auth Platform with an Internal audience and create a Web application OAuth client. Register these exact values:
In deploy/ec2/.env, replace example.edu with the Workspace domain and set BETTER_AUTH_CONNECTION_CLIENT_ID and BETTER_AUTH_CONNECTION_CLIENT_SECRET. Keep the connection ID as workspace so it matches the callback path.Follow Google Workspace for the complete console flow. If Auth0 is the approved broker instead, follow Auth0 and use connection ID auth0. Cognito, Okta, and Keycloak use Generic OIDC.
7

Validate and pull the release image

Define a short helper for the remaining commands:
OVERLAY_WEB_IMAGE must name the approved versioned release, such as ghcr.io/layernorm/overlay-web:0.1.0-rc.1. Do not use latest for a customer qualification run. The web, worker, scheduler, and migration roles all use the same immutable image.To build inspected or customized source instead, use the build override:
Continue with compose_source instead of compose for the remaining steps when using a source build. The override builds the tested enterprise runtime, including the web server, worker, scheduler, and migration tooling.
8

Run migrations once

Migrations must complete before any long-running service starts:
The command must exit with status 0 and report that both app-data and Better Auth migrations are current. Do not run migrations from every web, worker, or scheduler container.
9

Start web, worker, and scheduler

Wait for web to become healthy. Worker and scheduler logs should show a successful schema check and regular runtime activity without restart loops.
10

Open the SSH tunnel

From the operator’s computer, open a second terminal:
Keep that terminal open and visit http://localhost:3000. The browser and IdP see a localhost origin while the application runs inside the customer VPC.

Verify the sanity check

Run these commands on EC2:
Then verify in the browser:
  1. A signed-out user sees one Continue with Google option on a single line.
  2. Sign-in returns to /app/chat.
  3. /api/auth/session reports authenticated: true and emailVerified: true.
  4. Create a conversation and send one model request.
  5. Rename the conversation and refresh the page.
  6. Restart only the web container with compose restart web.
  7. Refresh again and confirm the conversation and title persist.
  8. Sign out and confirm the session is cleared.
  9. Confirm the browser network and container logs contain no Convex URL.
Expected capability signals include provider: "postgres" and requiresConvexClient: false. Features disabled by the sanity profile should not appear as usable navigation or routes.

Troubleshooting

Stop and remove the sanity host

Delete the disposable EC2 instance, databases, Redis deployment, security-group rules, snapshots, and OIDC callback when they are no longer needed. Retain only the redacted result, release commit, and approved evidence.

Paste into a coding agent

Paste the following only after authenticating the agent through the customer’s approved AWS mechanism:

Next: Deploy On AWS

Replace the single EC2 host with the qualified ECS, RDS, Redis, S3, Secrets Manager, and CloudWatch architecture.