Deployment
Running mem-port somewhere other than your own machine.
Nothing on this page is required to use mem-port. The daemon is built to run on localhost, where it needs no database, no accounts, and no configuration at all — that is still the default and still the recommended way to use it. This page is for the other case: putting mem-port on a host that more than one person, or more than one machine, can reach.
A networked database becomes required
The default embedded database writes to local disk. That is wrong anywhere the filesystem is ephemeral or more than one instance runs: you lose data when a container is replaced, and replicas silently diverge. Point mem-port at hosted SurrealDB or at Postgres instead.
The loopback boundary goes away
On 127.0.0.1 the operating system decides who can reach the daemon. On any other interface nothing does, so authentication switches on automatically with the exposure. A deployment is closed by default rather than depending on whoever wrote the deploy config.
Authentication follows exposure
There is no flag to remember. Auth is off on loopback, where the OS is already the boundary, and required on any other interface, where there is none. MEM_PORT_AUTH overrides it in both directions — set it to develop against the real thing locally, or to turn auth off when something in front is already authenticating.
Because a deployment binds a real interface, it needs a bootstrap admin or the daemon refuses to start rather than coming up and rejecting every login. The supplied manifests require the password instead of defaulting it, on the reasoning that a shipped default admin password is exactly the kind of thing that survives into production.
Then sign in at /admin to create workspaces, add users, issue their API keys and grant access. A workspace is one isolated knowledge graph, and its name is what clients send as library-id.
MEM_PORT_ADMIN_USER=admin
MEM_PORT_ADMIN_PASSWORD=<a real secret, from Secret Manager>Used only while no admin exists — leaving it set cannot reset a password or resurrect a deleted account.
- Clients send two headers and nothing else about them changes: Authorization: Bearer <the user's key>, and library-id: <a workspace they were granted>.
- API keys are shown once and stored only as a hash. Revoke and reissue when one needs rotating.
- Being an admin is not data access. Admins decide who may reach what, which is a different power from reading it, so a stolen admin password exposes the account model rather than every knowledge graph. An admin who wants a workspace grants it to themselves, visibly.
- The panel serves its own documentation at /admin/docs, with copy-pasteable client configuration for the URL the admin actually reached it on, and a read-only graph explorer per workspace — the quickest way to check whether a newly connected client is writing anything.
- Nothing rate-limits admin logins yet.
Pick a storage engine
mem-port ships two drivers behind one storage contract. Nothing above that contract can tell them apart, so this is an operational choice rather than a product one — pick the database you would rather operate.
Embedded SurrealDB
The default. Right for a personal daemon on one machine, and wrong for anything deployed.
Graph storage and vector search in the same process, writing to a file under the data directory. Nothing to install and nothing to run alongside it. It is the only option here that loses data on an ephemeral filesystem, which is why a deployment needs one of the other two.
- No external service
- Single instance only
Hosted SurrealDB
Right if you want the same engine the embedded default uses, operated as a server.
Surreal Cloud, or SurrealDB on a VM. Each library-id gets its own database inside the configured namespace. All three constraints below are checked at startup, so a misconfiguration fails once with an explanation rather than on every tool call.
TerminalMEM_PORT_DB_URL=wss://your-instance.surreal.cloud \ MEM_PORT_DB_USER=root \ MEM_PORT_DB_PASS=... \ mem-port serve- SurrealDB 3.0.0 or newer — sessions and transactions are both 3.0 server-side features, and mem-port needs both: a forked session per library-id for tenancy, and a transaction for import_library. A 2.x server connects fine and then fails on every request.
- ws:// or wss:// only — the HTTP engine supports neither feature at any version, so an http(s):// URL is rejected outright.
- A root- or namespace-level user — mem-port creates a database per library-id and defines its schema on first use, which a database-scoped user cannot do.
Postgres + pgvector
Right if you already operate Postgres, or want a managed service with backup and replication tooling you already know.
Each workspace gets its own Postgres schema, so isolation is structural rather than a WHERE clause nobody must forget. On GCP this usually means Cloud SQL, which is less work than running SurrealDB on a VM.
Terminalnpm install pg # optional dependency, only for this driver MEM_PORT_DB_URL=postgres://user:pass@host:5432/memport mem-port serve- The pg package is an optional dependency, imported lazily — install it only for this driver.
- pgvector is required and checked at connect time, because every search mem-port offers is a cosine similarity over an embedding. mem-port attempts CREATE EXTENSION itself, which works on most managed services where it is available but not yet enabled.
The two drivers are interchangeable, and that is enforced rather than claimed: one test seeds the same fixture through both engines and asserts every read tool returns byte-identical output. Not covered by it yet: a real managed Postgres, as opposed to the pgvector Docker image, and the admin path against Postgres rather than SurrealDB.
Where to run it
A container image, a Compose stack per database engine, and a Cloud Run service definition live under deployments/. All of them default to closed at the network layer as well as in the daemon — Compose publishes on loopback, Cloud Run uses internal ingress with invoker auth required — on the reasoning that defence in depth is cheap and an exposed admin panel is worth one more lock.
Docker Compose
The quickest way to run the real thing: a database with a volume, and mem-port pointed at it.
Two stacks, one per engine. Each brings its database up and serves the admin panel at /admin. Set the bootstrap admin password first or the stack will not start.
Terminalexport MEM_PORT_ADMIN_PASSWORD='local-dev-password' # SurrealDB docker compose -f deployments/docker/docker-compose.yml up --build # or Postgres docker compose -f deployments/docker/docker-compose.postgres.yml up --build- If a locally installed mem-port already holds 8787, publish elsewhere: MEM_PORT_HOST_PORT=8799 docker compose -f deployments/docker/docker-compose.yml up --build
Cloud Run
Right for a managed deployment on GCP, with Cloud SQL as the usual database.
A service definition plus a deploy script. It needs a database it can reach and two secrets — the database password and the bootstrap admin password — before the first deploy. The script builds linux/amd64 explicitly, because an image built on Apple Silicon is arm64 by default and fails to start on Cloud Run with an exec-format error that never mentions architecture. Images are tagged with the git short SHA, so a rollback names an exact build rather than chasing :latest.
TerminalPROJECT_ID=my-project REGION=us-central1 ./deployments/gcp/deploy.sh- The values in cloudrun.yaml are chosen, not defaults: containerConcurrency 8, because Node is single threaded and embedding inference is CPU-bound, where the platform default of 80 queues work behind one busy request.
- memory 1Gi, because the ONNX runtime plus the model does not fit comfortably in 512Mi.
- CPU throttling off, because CPU is throttled to near zero between requests by default, which can drop an idle WebSocket to SurrealDB.
- minScale 1, which avoids paying model load and the WebSocket handshake on the first request after an idle period. Set it to 0 to trade cold starts for cost.
Treat the manifests as stale after any change to auth, config or packaging
The deployment files have now been wrong twice for the same reason: verified once when written, then silently invalidated by a later change. 1.0.0 and 1.0.1 both shipped Compose files that refused to start, because authentication became required for anything binding a real interface after those files were written and checked. 1.0.2 fixed them. Nothing builds the image or starts the stacks automatically yet, so that class of bug is not closed — it is only fixed for now.
Every environment variable
All 21 variables the daemon reads, grouped as .env.example groups them. Real environment variables always win over a .env file, so a deployed container needs no .env at all — it takes its configuration from the platform.
Server
Where the daemon listens, and where its state lives.
- MEM_PORT_PORT
- 8787
- HTTP port. PORT is also honoured, since container platforms inject it
- MEM_PORT_HOST
- 127.0.0.1
- Interface to bind. Loopback is a security boundary, not a preference — set 0.0.0.0 only where something else enforces who may connect
- MEM_PORT_DATA_DIR
- OS app-data dir
- Where the embedded database and the cached embedding model live
- MEM_PORT_ENV_FILE
- ./.env
- Where to load the env file from. Also settable as mem-port serve --env-file <path>
Database
The driver is normally inferred from the URL scheme; the rest applies to whichever engine that selects.
- MEM_PORT_DB_URL
- surrealkv://<data-dir>/memport.db
- Database URL. surrealkv:// and mem:// are embedded, ws:// and wss:// are hosted SurrealDB, postgres:// is Postgres
- MEM_PORT_STORE
- inferred from the URL
- Which driver to use. Set it only to have a mismatch reported rather than inferred
- MEM_PORT_DB_USER / MEM_PORT_DB_PASS
- none
- Credentials. Required for a hosted SurrealDB server, which must be a root- or namespace-level user
- MEM_PORT_DB_TOKEN
- none
- Bearer token, as an alternative to user/password and mutually exclusive with them
- MEM_PORT_DB_NAMESPACE
- memport
- Namespace holding one database per library-id
- MEM_PORT_DB_PREFIX
- none
- Prefix for tenant database names on a shared cluster, or for the per-workspace schema names under Postgres
- MEM_PORT_DB_MAX_SESSIONS
- 256
- Cached per-library SurrealDB sessions before the least recently used is closed. Per instance, so N instances mean the cluster sees N times this
- MEM_PORT_DB_POOL_SIZE
- 10
- Postgres connection pool size
Authentication and the admin panel
Unset, these follow the bind. Set them to override it, or to supply the first admin.
- MEM_PORT_AUTH
- follows MEM_PORT_HOST
- off or required. Unset, it is off on loopback and required on any other interface
- MEM_PORT_ADMIN_USER
- none
- Creates the first admin, and only while no admin exists
- MEM_PORT_ADMIN_PASSWORD
- none
- That admin's password. Required rather than defaulted, deliberately
- MEM_PORT_SESSION_TTL_HOURS
- 12
- How long an admin panel login lasts
- MEM_PORT_PUBLIC_URL
- the Host header
- The URL clients should be pointed at, shown on the panel's docs page. Set it behind a proxy that rewrites Host, where the header would otherwise give a useless internal address
Embeddings
Semantic search runs on a local ONNX model, with no API key and no external call.
- MEM_PORT_EMBEDDING_MODEL
- Xenova/all-MiniLM-L6-v2
- Local embedding model id (reserved for future use)
- MEM_PORT_MODEL_CACHE_DIR
- <data-dir>/models
- Where the model is cached. In a container, point this at weights baked in at build time so a cold start does not download them
UI
Whether the read tools declare a renderable results panel.
- MCP_APPS / MEM_PORT_MCP_APPS
- on
- Set to 0 to stop the read tools declaring an MCP Apps UI resource
.env.example in the repo is the committed reference, and carries the reasoning behind each default in full.
- deployments/The container image, both Compose stacks, and the Cloud Run service definition
- Configuration reference.env.example — every MEM_PORT_* variable, with the reasoning behind each default
- Cloud Run guidedeployments/gcp/README.md — what to set up before the first deploy, and the sizing notes
- Release notesWhat landed in 1.0.0, 1.0.1 and 1.0.2, including why the deployment files did not work
Everything on this page is copied from mem-port’s own deployment files and configuration reference. Running it locally needs none of it.