An AI answer engine with a generative UI: ask a question, get a cited answer streamed back with rich inline components, backed by Exa neural search and managed Postgres.
This template deploys Morphic on Render as a Docker web service plus a managed PostgreSQL database. It swaps the upstream Docker Compose stack (self-hosted Postgres, Redis, and SearXNG) for Exa search and Render Postgres, so you run two billable resources instead of four containers. Bring one Exa key and one LLM key and you have a private, persistent AI search app on your own domain.
- Why deploy Morphic on Render
- Use cases
- What gets deployed
- Quickstart
- Configuration
- Cost breakdown
- Customization
- Operations
- Upgrading
- Troubleshooting
- FAQ
- Security
- Caveats and limitations
- Credits and license
- Two resources, not four: the upstream Compose file runs Postgres, Redis, and SearXNG alongside the app. This template uses Exa for search and Render's managed Postgres for chat history, so there is no Redis or search container to babysit.
- Chat history wired for you:
DATABASE_URLis injected from themorphic-dbPostgres instance via a Blueprint reference. You never copy a connection string. - Standard plan by default: Morphic builds Next.js 16 and runs migrations on boot. The Blueprint ships the
standard(2 GB) plan becausestarter(512 MB) OOMs during the Next.js build and start, which shows up as "No open ports detected". - One project in your dashboard: the web service, database, and env group land in a single named Render project instead of loose resources scattered across your workspace.
What people build with this template:
- A private research assistant: ask multi-step questions and get answers with real citations, kept off shared SaaS chat tools.
- A team knowledge search page: put it on an internal subdomain so your team searches the web through one shared, logged endpoint.
- A generative-UI demo: show off streamed components (image grids, follow-up chips, structured answers) on top of your own model keys.
- A provider comparison harness: wire OpenAI, Anthropic, and Google keys at once and switch models per query from the selector.
flowchart LR
user["Browser"] -->|HTTPS| web["morphic<br/>web service (Docker, Standard)"]
web -->|chat history| db[("morphic-db<br/>Postgres 17")]
web -->|neural search| exa["Exa API"]
web -->|completions| llm["LLM provider<br/>(OpenAI / Anthropic / Google)"]
| Resource | Type | Plan | Purpose |
|---|---|---|---|
morphic |
Web service (Docker) | Standard | Next.js 16 app: chat UI, API routes, migrations on boot |
morphic-db |
PostgreSQL 17 | basic-256mb | Persists chat history and shared chats |
morphic-render |
Env group | : | Shared non-secret config (search provider, anonymous mode, TLS flag) |
Region: oregon (override the region fields in render.yaml if you want another one; keep the web service and database in the same region so they talk over the private network).
Exa and your LLM provider are external APIs. They are billed by those providers, not by Render.
- Click Deploy to Render. Render forks this template into your GitHub account and reads
render.yaml. - Get an Exa key from dashboard.exa.ai and at least one LLM key (see Required secrets).
- On the Apply screen, enter
EXA_API_KEY. Optionally paste an LLM key now, or add it on the service after the first deploy. - Click Apply. The first deploy takes about 5 to 10 minutes (Docker build, Next.js compile, then Drizzle migrations on container start).
- When the
morphicservice is Live, open its*.onrender.comURL and run a query.
If the model selector is empty on first load, you have not set an LLM key yet. Add one on the morphic service (see below) and Morphic detects available models automatically.
You set these during the Apply step, or on the morphic service afterward. A missing Exa key means search returns errors; a missing LLM key means the model selector is empty.
| Env var | What it's for | How to get it |
|---|---|---|
EXA_API_KEY |
Neural web search (the Blueprint sets SEARCH_API=exa) |
dashboard.exa.ai |
| One LLM key | Answer generation. Set one of the keys below. | provider console (links below) |
Pick one LLM provider to start (you can add more later):
| Env var | Provider | Where to get it |
|---|---|---|
OPENAI_API_KEY |
OpenAI | platform.openai.com/api-keys |
ANTHROPIC_API_KEY |
Anthropic Claude | console.anthropic.com |
GOOGLE_GENERATIVE_AI_API_KEY |
Google Gemini | aistudio.google.com/apikey |
AI_GATEWAY_API_KEY |
Vercel AI Gateway (multi-provider) | vercel.com/ai-gateway |
The LLM keys are not declared in render.yaml. Morphic supports many providers and only needs one, so forcing a specific key at Apply would be wrong. Add whichever you use on the morphic service → Environment. Do not leave placeholder values like REPLACE_ME: an invalid key fails at request time with "We could not generate a response".
None. This template does not generate any secrets. Morphic in anonymous mode does not sign sessions, so there is no secret to create or rotate.
The Blueprint sets these for you. You never type them.
| Env var | Source |
|---|---|
DATABASE_URL |
morphic-db connection string (fromDatabase) |
DATABASE_RESTRICTED_URL |
Same morphic-db instance (upstream reads both) |
Database references cannot live in an env group, so these are attached directly to the morphic service in render.yaml.
Common changes after deploying. All are env vars you add or override on the morphic service or in render.yaml. The Blueprint's morphic-render env group already sets the defaults in the first four rows.
| Env var | Default | What it does |
|---|---|---|
SEARCH_API |
exa |
Search backend. tavily, firecrawl, or searxng are also supported (each needs its own key). |
ENABLE_AUTH |
false |
true turns on multi-user Supabase auth (see Customization). |
ANONYMOUS_USER_ID |
anonymous-user |
The shared user id all chats belong to in anonymous mode. |
NODE_TLS_REJECT_UNAUTHORIZED |
0 |
Required for Render's managed Postgres TLS with upstream's strict Node verification. See Security. |
TAVILY_API_KEY |
: | Set with SEARCH_API=tavily to use Tavily instead of Exa. |
NODE_ENV |
production |
Standard Next.js production flag. |
Full upstream config reference: Morphic CONFIGURATION.md.
| Resource | Plan | Monthly cost |
|---|---|---|
morphic |
Standard | $25 |
morphic-db |
basic-256mb | $6 |
| Total | $31 |
Render's full pricing: render.com/pricing. Exa and your LLM provider bill you separately for usage.
- Cheaper: drop the web service to
starteronly if you also reduce the build footprint; by defaultstarterOOMs on the Next.js build, sostandardis the realistic floor for this app. The database can stay onbasic-256mb. - Scale up: bump the
morphicplan for more memory and CPU under heavy concurrent search, and movemorphic-dbto a larger plan (or add a high-availability standby) as chat history grows.
This template tracks the upstream default branch through the files committed in this repo. To lock the app to a known-good commit, check out that commit of miurla/morphic into your fork before deploying, or keep your fork on a branch you control and merge upstream deliberately. The Docker build always uses the code in your fork, so your fork is the version pin.
In the Render dashboard, open the morphic service → Settings → Custom Domains → Add. Render issues and renews TLS automatically. DNS setup is documented at Render custom domains.
Exa is the default. To use Tavily instead:
# render.yaml, in the morphic-render env group
- key: SEARCH_API
value: tavilyThen add TAVILY_API_KEY as a sync: false secret on the service. Firecrawl (FIRECRAWL_API_KEY) and self-hosted SearXNG work the same way. Only the provider matching SEARCH_API needs a key.
Anonymous mode is on by default: everyone shares one user id and there are no logins. To run Morphic for multiple accounts, set ENABLE_AUTH=true and add the Supabase env vars from the upstream auth docs:
- key: ENABLE_AUTH
value: "true"
- key: NEXT_PUBLIC_SUPABASE_URL
sync: false
- key: NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY
sync: false
- key: SUPABASE_SECRET_KEY
sync: falseMorphic supports Cloudflare R2 / S3-compatible uploads when the R2_* and S3_ENDPOINT vars are set. Leave them unset to keep uploads disabled. See .env.example for the full list.
morphic-db gets Render's automatic daily Postgres backups with point-in-time recovery on paid plans. Chat history lives entirely in Postgres, so the database backup is your recovery point. There is no persistent disk on the web service, and nothing on the container filesystem needs backing up (it is rebuilt on every deploy).
Open the morphic service in the dashboard for CPU, memory, and request metrics. The Blueprint sets healthCheckPath: /, so Render marks a deploy healthy only after the app answers on /. Watch memory during the first few deploys: if it climbs toward the plan ceiling, that is the OOM signal to keep it on standard or larger.
The web service is stateless (all state is in Postgres), so it scales horizontally. Raise scaling.numInstances or configure autoscaling on the morphic service. Zero-downtime deploys work because there is no disk pinning the service to one instance. The database is single-primary; add a read replica or HA standby from the database page if you need it.
In the Render dashboard, open the morphic service → Logs. On first deploy, confirm the migration line (Drizzle reports completed migrations) and that the server bound to $PORT. CLI: render logs --resources <service-id> --tail.
This repo is a fork of the Morphic source plus a Render render.yaml. To pull in new upstream work:
- Add the upstream remote once:
git remote add upstream https://github.com/miurla/morphic.git. git fetch upstream && git merge upstream/main(resolve conflicts, most likely inDockerfile,package.json, or config).- Push to your fork. Render auto-deploys the new commit.
Keep your render.yaml and this README during the merge; they do not exist upstream.
Watch the upstream changelog and releases before merging across major versions. Notable things to check:
- Schema changes: Morphic runs Drizzle migrations on container start, so a schema change deploys automatically. Take a database backup before a major upgrade so you can roll back.
- Env var renames: if upstream renames or adds a required variable, update the
morphic-rendergroup or the service env vars to match.
Check the deploy logs under Events. The most common cause is memory pressure during the Next.js build. This template already uses the standard plan for that reason; if you lowered it to starter, the build or start step gets OOM-killed and the deploy fails. Restore plan: standard in render.yaml.
The process died before binding $PORT, almost always an out-of-memory kill during Next.js startup on an undersized plan. Bump the plan back to standard (or larger) and redeploy. Confirm in the logs that the server prints its listening line and that migrations completed first.
Confirm EXA_API_KEY is set on the morphic service and that SEARCH_API=exa (it is, via the env group). If you switched SEARCH_API, make sure the matching provider key is set.
No valid LLM key. Add one of OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, or AI_GATEWAY_API_KEY on the service and remove any placeholder value. The model selector populates once a working key is present.
Ensure NODE_TLS_REJECT_UNAUTHORIZED=0 is set (the morphic-render env group sets it). Upstream Morphic uses strict Node TLS verification, and Render's internal Postgres URL presents a platform CA that Node rejects without this flag. See Security for the hardening note.
Confirm morphic-db is Available and that the migration line appears in the morphic logs. If migrations did not run, the tables do not exist yet and history cannot persist.
- Service-level logs: dashboard → Logs (or
render logs --resources <id> --tail) - Deploy-level logs: dashboard → Events → click the failed deploy
- Template wiring bugs: open an issue in render-examples/morphic
- Application bugs: open an issue upstream
Not comfortably. The Next.js build and runtime need more than the free/starter 512 MB, and free Postgres expires after 30 days. Use standard for the web service and a paid basic-256mb database for anything you want to keep.
No. Only EXA_API_KEY is prompted at Apply. Add an LLM key on the service whenever you are ready; Morphic detects available models from whatever keys are present.
Yes. Set SEARCH_API to tavily, firecrawl, or searxng and add that provider's key. See Switch search providers.
In the morphic-db Postgres database, not on the container. Redeploys and restarts do not lose it. Deleting the database deletes the history.
By default no: it runs in anonymous single-user mode where everyone shares one user id. Enable Supabase auth to support real accounts (see Customization).
Upstream bundles Postgres, Redis, and SearXNG as containers. This template uses Render's managed Postgres and the hosted Exa API, and drops Redis. Fewer moving parts, and the database is backed up for you.
- Encryption at rest:
morphic-db(managed Postgres) is encrypted at rest by Render, including backups. - Encryption in transit: TLS terminates at the
*.onrender.comhostname, and traffic tomorphic-dbuses Render's private network. - Network exposure: only the
morphicweb service is public. The database is reachable over the private network from the web service and is not exposed publicly (ipAllowList: []). NODE_TLS_REJECT_UNAUTHORIZED=0: this disables Node's TLS certificate verification process-wide inside the container. It is set to make upstream's strict verification work with Render's internal Postgres CA without patching app code. It affects only server-side Node calls inside your container, not the browser-facing TLS. For production hardening, fix certificate verification in the app's database layer and remove this flag.- Secret rotation:
EXA_API_KEYand LLM keys are safe to rotate anytime (update the value on the service and redeploy). There are no generated secrets to worry about. - Reporting vulnerabilities: template wiring issues → this repo; application issues → upstream.
NODE_TLS_REJECT_UNAUTHORIZED=0is a tradeoff. It relaxes TLS verification inside the container to connect to Render Postgres without app changes. Harden this before treating the deploy as production-grade (see Security).starteris not enough. The Next.js build OOMs on 512 MB, sostandardis the practical minimum for the web service. This is why the Blueprint does not default tostarter.- This is a fork, not a thin wrapper. The full Morphic source lives in this repo, so upgrading means merging upstream and resolving conflicts rather than bumping an image tag.
- Anonymous mode by default. Every visitor shares one user id and one history until you enable auth. Do not put it on a public URL expecting per-user privacy.
- Single region. The web service and database default to
oregon. Multi-region read replicas are a Render Postgres feature you configure separately.
- Upstream: miurla/morphic : Apache-2.0
- This template: a fork of the upstream source plus a Render
render.yamland this README, distributed under the same Apache-2.0 license. - Template maintainer: ojusave
If this template helped you, star the upstream Morphic repo.
