Run Vault Cortex on Render from a button — no server to manage.
What you end up with:
- A copy of your vault on Render, kept current by Obsidian Sync in both directions.
- Any MCP client can search, read, and write it from anywhere.
- HTTPS, restarts, and a generated access token, handled by Render.
- One disk holding the vault copy, the search index, and the Sync connection.
Your notes' home stays Obsidian Sync and your own devices; delete the service and you lose only the copy.
Prefer your own VPS? Use the remote quickstart instead. Curious how the container is put together? ARCHITECTURE.md →.
Contents — Prerequisites · Deploy · Your URL and token · Sign in to Obsidian Sync · Security · First start · Connect · Verify · Updating · Restart, stop, delete · Config · Troubleshooting
- A Render account with a payment card on file. You don't need a paid Render plan — the free Hobby workspace works — but the server itself is paid: the Standard instance (1 CPU, 2 GB) and the 5 GB disk the Blueprint creates cost about $26 USD/month together, the instance billed by the second and the disk as its own line item (Render's pricing).
- An Obsidian Sync subscription
Render asks for a card first if your workspace has none on file, then reads
the Blueprint — Render's name for a ready-made deployment recipe stored in
the repository — and asks for a Blueprint Name (any name) and four
values before it creates anything. Only VAULT_NAME is required; the rest
are optional:
| Field | Value |
|---|---|
OBSIDIAN_AUTH_TOKEN |
Leave empty — you sign in through the setup page after deploy (see Sign in to Obsidian Sync). Or paste a token from npx vault-cortex@latest get-sync-token to skip the setup page |
VAULT_NAME |
Your vault's name, the same as it is in Obsidian |
VAULT_PASSWORD |
Only if your vault uses end-to-end encryption; otherwise leave empty |
TZ |
Your timezone as an IANA name (America/Toronto) — decides what "today" means for daily notes, task due dates, and memory timestamps. Leave empty for UTC |
Fill in at least the vault name before the first deploy. Click Deploy Blueprint. Render creates the service and disk, pulls the image, and starts the first deploy. Everything else — the MCP token, the public URL, the port, the storage layout — is set by the Blueprint.
-
URL: shown as a link at the top of the service page. Render builds it from the service name the Blueprint sets,
vault-cortex, plus four random characters, becauseonrender.comsubdomains are unique across all of Render and the bare name is taken. Expecthttps://vault-cortex-xxxx.onrender.com. Wherever this guide shows<host>, put your ownvault-cortex-xxxx.onrender.comaddress in its place:https://<host>/mcpbecomeshttps://vault-cortex-xxxx.onrender.com/mcp. -
Token:
MCP_AUTH_TOKENunder the service's Environment tab. Render generated it for you; your MCP client enters it once on the consent page.
When the service shows a green Live badge, sign in to Obsidian Sync through the setup page:
-
Copy
MCP_AUTH_TOKENfrom the service's Environment tab — click the eye icon to reveal it, then copy the value. -
Open the setup page: click the URL at the top of the service page — the server sends your browser straight to the sign-in page. (Adding
/setupto the address goes to the same place.) -
Paste the
MCP_AUTH_TOKENvalue in the token field. -
Enter your Obsidian account email and password. If you use two-factor authentication, the page asks for the code on the next step.
-
Wait for "Your vault is ready." The server signs in, validates your vault settings, writes the token, and restarts to download your vault and build the search index. The page follows the progress automatically. A vault of a few thousand notes takes two to three minutes; a large vault can take longer.
Once the page shows your MCP URL, the server is live and ready to connect.
Already have a token?
If you already have an Obsidian Sync token (from a previous deploy, from the
CLI, or from a manual login), paste it into the OBSIDIAN_AUTH_TOKEN field
on the Blueprint form — the container skips the setup page and starts
syncing immediately.
To generate a token from the command line:
npx vault-cortex@latest get-sync-tokenOr, if you don't have Node.js installed:
docker run --rm -it --entrypoint get-sync-token ghcr.io/aliasunder/vault-cortex:remoteOut of the box, every request to your instance travels over HTTPS to Render's edge and is checked by the server before any vault data moves:
- Always HTTPS. Render provisions and renews the certificate and terminates TLS at its edge; the container is never reachable directly.
- Nothing without the token.
/mcpanswers401to any request that lacks a valid token. OAuth clients get one through the consent page (full OAuth 2.1 with PKCE and refresh-token rotation); scripts sendMCP_AUTH_TOKENas a bearer header. The login, registration, and token endpoints are rate-limited per visitor address. - Encrypted at rest, snapshotted daily. The disk holding your vault, index, and Sync device state is encrypted, and Render snapshots it every 24 hours (kept at least seven days) — restore from Disks → Snapshots.
- Logs carry no secrets. The server logs note paths, headings, search queries, and error text — your vault's own metadata — never tokens, passwords, or note bodies.
What the server can't protect is the Render account that holds it: anyone who can open this service in the dashboard can read the environment variables, the logs, and the disk. Two steps close that, both from the dashboard:
- Turn on two-factor authentication for your Render account (Account Settings → Account Security). The environment variables hold the credentials that grant access to your vault.
- Keep the workspace to yourself. Members you invite can read every environment variable and every log line, search queries included.
Optional settings narrow what a connected client can do — change them
under Environment: READONLY_MODE=true removes every tool that writes
to the vault, and FILE_TOOLS_ENABLED=false or MEMORY_ENABLED=false hide
those tool groups entirely (see Configuration).
Changing MCP_AUTH_TOKEN ends every session: access tokens issued under
the old value stop working, stored refresh tokens become unusable, and each
client goes back through the consent page on its next request.
SECURITY.md describes what the server exposes and how
each part is hardened.
After you sign in on the setup page, the container restarts and runs the
full startup sequence: it logs in to Obsidian Sync, downloads your vault,
builds the search index, and then answers health checks and receives
traffic. A vault of a few thousand notes takes two to three minutes; Render
allows up to 15 minutes, and a large vault can take most of it. Until the
index is built, the URL answers 502 — that is Render waiting, not a
failure.
Watch the Logs tab. Lines prefixed [obsidian-sync] are the Sync setup
and download; [vault-cortex] lines show the storage layout and the public
URL the container derived (PUBLIC_URL derived from RENDER_EXTERNAL_URL);
the structured JSON lines are the MCP server. server started means the
deploy is about to go live.
The same three steps in every app:
- In Claude Desktop, claude.ai, Perplexity, or any app with an Add custom
connector (remote MCP server) option, paste
https://<host>/mcp— the MCP URL the setup page showed when it finished. Leave Client ID and Secret empty. - A consent page opens in your browser. Approve it with the
MCP_AUTH_TOKENRender generated. - Done — the client renews its own access from then on. Under the hood that is full OAuth 2.1, with PKCE, dynamic client registration, and refresh-token rotation on a 60-day sliding expiry.
Other clients:
-
Claude Code — one command instead of a settings screen; the same consent page opens:
claude mcp add --scope user --transport http vault-cortex https://<host>/mcp
-
Scripts and MCP Inspector — send
MCP_AUTH_TOKENas anAuthorization: Bearerheader.
Client-by-client details are in the remote quickstart's Connect your MCP client.
Open https://<host>/healthz in your browser — it answers {"ok":true}.
Then, in your MCP client, run a search — results come from your vault.
The same check from a terminal, if you prefer:
curl https://<host>/healthz
# → {"ok":true}Image services on Render don't redeploy on their own when a new image is
published. To update: open the service, click Manual Deploy → Deploy latest
reference. Render pulls the current :remote image and restarts the
container. Your vault and search index stay on the disk, so the update takes
a minute or two: the new image is pulled, then the container starts the same
way a restart does.
Before you redeploy, skim GitHub Releases — each release lists what changed since the last one, and an entry marked Breaking change means a setting or behavior you rely on may have moved.
- Restart — on the service, Manual Deploy → Restart service. Same container, same disk.
- Stop — Settings → Suspend Service. The instance stops billing; the disk and everything on it stay, and Render bills disks separately at $0.25 USD per GB per month — about $1.25 a month for the 5 GB disk — so expect that charge to continue until the service is deleted. Resume picks up where it left off.
- Delete — two steps, because the Blueprint would otherwise re-create the service: from the dashboard, open Blueprints in the left nav, open your Blueprint → Settings → Disconnect Blueprint, then the service → Settings → Delete Service. Deleting the service deletes the disk too. Your vault in Obsidian Sync is untouched — the container only held a copy.
The Blueprint sets these; change them under the service's Environment tab (each change triggers a redeploy):
| Variable | Value | What it does |
|---|---|---|
STORAGE_ROOT |
/persist |
Where the disk is mounted — the vault, search index, Sync device state, and logs live under it. Must not contain *, ?, or [. Leave as is. |
PORT |
8000 |
The port the image listens on. Leave as is. |
DEVICE_NAME |
vault-cortex |
The device name that labels this container's changes in Obsidian's sync log. |
TRUST_PROXY_HOPS |
2 |
Render's network puts two proxies between a visitor and the container; this lets the server see the visitor's real address in its logs and rate limits. |
MCP_AUTH_TOKEN |
generated | Your MCP client's token. Change it here to rotate it — every connected client re-authorizes. |
OBSIDIAN_AUTH_TOKEN |
from setup page | Obsidian Sync login. Set by the setup page on first deploy (written to the disk). To sign in with a different account, set a new token here — it always overrides the file on the disk. |
VAULT_NAME |
yours | The vault this container syncs. |
VAULT_PASSWORD |
yours / empty | End-to-end encryption password, if your vault has one. |
TZ |
yours / empty | Timezone for daily notes, task due dates, and memory timestamps. Empty means UTC. |
MEMORY_ENABLED |
true |
The About Me/ memory layer and its tools. Set false to hide them and skip creating the folder. |
EMBEDDING_ENABLED |
true |
Semantic search. Set false to skip the models and use keyword search only — the container fits in much less memory. |
READONLY_MODE |
false |
Set true to hide every tool that changes the vault — clients can only read and search. |
FILE_TOOLS_ENABLED |
true |
vault_read_file and vault_list_files. Set false when Obsidian Sync has attachment syncing off. |
SYNC_MODE |
bidirectional |
Sync direction: bidirectional, pull-only (edits made on the server stay on the server and are never uploaded), or mirror-remote (Obsidian Sync overwrites edits made on the server, so the server always matches your vault). An invalid value stops the container at boot. |
CONFLICT_STRATEGY |
merge |
Obsidian Sync conflict resolution: merge integrates changes automatically; conflict writes a separate conflict file. |
SYNC_EXCLUDED_FOLDERS |
(empty) | Folders to leave out of sync, comma-separated — the same list as Obsidian's Sync → Excluded folders. Empty excludes nothing. |
SYNC_FILE_TYPES |
(empty) | Attachment types to sync, comma-separated — the same toggles as Obsidian's Sync → Selective sync. Valid values: image, audio, video, pdf, unsupported. Empty (default): image,audio,video,pdf. The text files Vault Cortex reads (CSV, JSON, TXT, XML, LOG, YAML) fall under unsupported, Obsidian Sync's name for its Sync all other types category; SVG syncs as image, and notes, canvases, and bases always sync. To read those text files, set it to image,audio,video,pdf,unsupported (the value replaces the default list, so keep the four defaults) and turn on Sync all other types in Obsidian's Sync settings on the device that has them. An invalid value is skipped with a warning in the logs, and the default types sync instead. |
LOG_DIR |
derived | Log files, kept 90 days on the disk — the platform's own log viewer keeps only the last 7 days on Hobby plans. Set none to keep logs in the platform viewer only. |
SYNC_MODE, CONFLICT_STRATEGY, SYNC_EXCLUDED_FOLDERS, and
SYNC_FILE_TYPES are the settings most worth changing on a hosted instance;
the Blueprint pre-fills them with the image defaults so you can edit them in
place. LOG_DIR is not pre-filled — the container derives it at every start,
and none is the only value worth setting by hand. Every other optional setting uses the same names as the remote
quickstart's Configuration table — add it as a
new environment variable.
Don't set PUBLIC_URL or VAULT_PATH — the container derives
them from STORAGE_ROOT and Render's own address variable at every start.
Instance size and disk size are changed under Settings (Render can grow a disk, never shrink it).
The setup page shows an error after signing in. The page tells you what went wrong:
- login was rejected — wrong email, password, or two-factor code. Try again on the same page.
- Vault "X" was not found — the vault name doesn't match Obsidian Sync
exactly (it is case-sensitive). Fix
VAULT_NAMEunder the service's Environment tab and save — saving redeploys the service. - Vault password required — the vault is end-to-end encrypted and
VAULT_PASSWORDis missing. Add it under Environment and save. - Wrong vault key — the vault password is incorrect. Fix
VAULT_PASSWORDunder Environment and save.
VAULT_NAME is not set in the logs. Add it under Environment and
save — saving redeploys the service.
The deploy timed out waiting for the health check. Render allows 15
minutes from container start. The first start of a large vault — download
plus search-index build — can exceed that. Click Manual Deploy → Deploy
latest reference: the files and index that already reached the disk are
reused, so the second attempt is much faster. For very large vaults, set EMBEDDING_ENABLED=false
for the first deploy and remove it once the vault has synced.
A redeploy took as long as the first deploy and downloaded the whole vault
again. The disk was replaced, or /persist/config was removed, so the
container started over with a fresh Obsidian Sync connection. Nothing needs
cleaning up. To switch to another vault, don't remove /persist/config on
its own: follow the last step of the next entry.
This install is set up to sync a different vault than VAULT_NAME. The
container refused to start because the disk holds the Obsidian Sync setup
for another vault, usually after VAULT_NAME was changed. Starting would
merge the two vaults' notes. Nothing was changed, and the log line after it
names both vaults. Then do one of these:
- If
VAULT_NAMEis a mistake, set it back under Environment and save. - To serve the other vault as well, deploy the Blueprint again for it, as a
separate service with its own disk, and leave this one on its original
vault. The new service has its own URL and
MCP_AUTH_TOKEN, so connect it as its own server in your MCP client. - To switch to the new vault, deploy the Blueprint again for it, then delete
this service (see Restart, stop, delete). The new
service downloads the new vault like a first deploy, and the old vault
stays in Obsidian Sync. Connect your MCP client to the new service's URL
with its
MCP_AUTH_TOKEN. Removing only/persist/configwould upload the old vault's notes into the new one.
The vault is empty but this device has previously synced. The
container refused to start because the vault directory on the disk is empty
while the device state says a sync already happened — starting would push
deletions to your other devices. This happens only if something removed
/persist/vault by hand. Restore the disk from a snapshot (Disks → Snapshots
on the service page), or remove /persist/config as well to start over with
a fresh device.
First sync failed with N file(s) still waiting to download. The
container stopped on purpose. Some files in your Obsidian Sync vault haven't
downloaded to the server yet, and starting two-way sync without them would
delete them from your other devices. This usually happens right after you
change SYNC_FILE_TYPES or SYNC_EXCLUDED_FOLDERS, when many files need to
download at once.
- Click Manual Deploy → Deploy latest reference. If the failure was a dropped connection, the next start picks up where this one stopped.
- If it stops again with the same message, set
SYNC_MODEtopull-onlyunder Environment and save. The server starts and keeps retrying the downloads, andpull-onlynever deletes anything from Obsidian Sync. Edits made on the server in the meantime stay on the server. - Later, set
SYNC_MODEback tobidirectionaland save. If the container stops with this message again, the downloads haven't finished yet, so switch back topull-onlyand try again later.
If it still stops after several hours, one file is probably failing to
download every time. In the logs, the Downloading line just before
Sync error: names it. Then do one of these:
- Fix the file in Obsidian on another device.
- Delete it in Obsidian on another device. The deletion reaches every device. Obsidian Sync's version history (Settings → Sync → Deleted files) keeps a deleted note for 1 month on Sync Standard and 12 months on Sync Plus, and a deleted attachment for two weeks on either plan.
- Add its folder to
SYNC_EXCLUDED_FOLDERS. That keeps every file in the folder off the server, not just the failing one.
N can be higher than the number of files you expect. It includes files that
your SYNC_FILE_TYPES, SYNC_EXCLUDED_FOLDERS, or SYNC_CONFIGS settings
leave out, and a sync that completes skips those.
ob sync-config --mode '…' failed. SYNC_MODE isn't one of
bidirectional, pull-only, or mirror-remote. Fix it under
Environment and save — saving redeploys the service.





