Files
ditto.site/docs/DEPLOY.md
T
Samraaj BathandClaude Fable 5 c35680eb13 Docs: job event stream, app-preview routes, migration notes
SERVICE.md documents the /events polling endpoint, the event persistence
model, the in-memory backend's enqueue-202 contract, and the app-preview
route; DEPLOY.md notes the job_events migration and that db:migrate is
journal-tracked/idempotent (with the manual-psql alternative).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 15:16:08 -07:00

3.8 KiB
Raw Blame History

Deploy — Railway + Neon + R2

The service is two long-lived processes (an api and a worker) plus managed Postgres and S3-compatible storage. The recommended OSS-friendly stack:

  • Railway — hosts the api and worker services (one Railway service each, built from the Dockerfiles in docker/).
  • Neon — managed Postgres (the database and the pg-boss queue).
  • Cloudflare R2 (or AWS S3) — blob storage for binary assets + bundles. R2 has no egress fees, friendly for forks/self-host.

1. Database (Neon)

  1. Create a Neon project; copy the pooled connection string.

  2. Apply migrations (from CI/CD or locally):

    DATABASE_URL='postgres://...neon.../ditto_site?sslmode=require' npm run db:migrate
    

    Run this on every deploy that changes packages/db/migrations. Drizzle tracks applied migrations in the database, so re-running is a no-op for anything already applied. (Alternatively, apply a single migration by piping its SQL through psql/the Neon CLI — but prefer db:migrate, which also records the journal entry so future runs stay consistent.)

    Current migrations: 0000 core tables, 0001 signup tokens, 0002 job_events (structured clone-progress events behind GET /v1/clones/:id/events).

2. Blob storage (R2 / S3)

Create a bucket (e.g. ditto-site-artifacts) and an access key. Note the S3 API endpoint (R2: https://<accountid>.r2.cloudflarestorage.com).

3. Railway services

Create two services in one Railway project, both deploying this repo:

Service Dockerfile Scaling
api docker/api.Dockerfile light; autoscale on CPU
worker docker/worker.Dockerfile scale by queue depth (Playwright image)

Set these variables on both services:

DATABASE_URL=postgres://...neon.../ditto_site?sslmode=require
S3_BUCKET=ditto-site-artifacts
S3_ENDPOINT=https://<accountid>.r2.cloudflarestorage.com   # omit for AWS S3
S3_REGION=auto                                             # 'auto' for R2
S3_ACCESS_KEY_ID=…
S3_SECRET_ACCESS_KEY=…

API-only:

PORT=8787
PUBLIC_BASE_URL=https://api.yourdomain.com   # so MCP-returned URLs are absolute
API_KEYS=key_live_xxx,key_live_yyy           # require auth
RATE_LIMIT_PER_MINUTE=60
SIGNUP_ENABLED=true                          # optional: public API-key minting
SIGNUP_RATE_LIMIT_PER_HOUR=3
DEFAULT_SIGNUP_KEY_RATE_LIMIT=30
SIGNUP_DIRECT_ENABLED=false                  # recommended once verified email is configured
RESEND_API_KEY=re_...
SIGNUP_FROM_EMAIL=Ditto <hello@ditto.site>   # must be a verified Resend sender/domain
SIGNUP_VERIFY_URL=https://www.ditto.site/api-key
SIGNUP_TOKEN_TTL_MINUTES=30
SIGNUP_CORS_ORIGINS=https://ditto.site,https://www.ditto.site # browser origins allowed to call signup routes
# SSRF is on by default; do NOT set SSRF_ALLOW_LOOPBACK in prod.

Worker-only:

CACHE_STALE_AFTER=24h
ARTIFACTS_DIR=/data/artifacts   # only used if S3 is not configured
HARNESS_DIR=/data/harness       # isolated build harness for verify

4. Scaling notes

  • Capture workers parallelize freely (each clone = one headless Chromium, ~0.51 GB peak); scale horizontally by queue depth.
  • Verify is the heavy step (next build). Each worker container has its own filesystem, so its HARNESS_DIR is naturally isolated; for multiple workers on one host, give each a distinct HARNESS_DIR.
  • Per-job wall-clock caps and retries are handled by pg-boss; a compilerVersion bump invalidates the cache automatically.

5. CI

.github/workflows/ci.yml typechecks and runs the test suite on every push/PR with a Postgres service container + Chromium installed, so the DB and browser-gated tests run in CI. Heavy real-site benchmarks remain a manual/nightly concern; benchmark lists live in compiler/benchmarks/.