This is the full operator manual for deploying RustyRed via the Railway template. The README's "Deploy on Railway" section covers the happy path; this document covers everything else.
For the threat model and reporting process, see ../SECURITY.md.
For the env-var quick reference, see the README's "Environment variable
reference" section or ../.env.example.
A single Railway service running rusty-red-graph-server (the
rustyred-server crate's binary) with:
- A persistent volume mounted at
/app/data/rusty-red(1 GiB default). - Auth required by default.
RUSTY_RED_API_TOKENSis pre-filled with a fresh 64-character hex secret generated by Railway's template variable function — the equivalent ofopenssl rand -hex 32— scoped tograph:read|graph:write|context:read|admin:read|federation:write. - MCP exposed at
/mcpin read-only mode. - Healthcheck against
/ready. - AOF-every-second durability with a snapshot every 1000 writes.
- No companion services. No Redis, no FalkorDB, no Memgraph.
- Click the Deploy on Railway badge in the repository README.
- Railway opens the template page. Confirm the listed variables. The
pre-generated
RUSTY_RED_API_TOKENSvalue will look like:<64-hex-chars>=graph:read|graph:write|context:read|admin:read|federation:write. - Copy the token before clicking Deploy. Railway will still show it in the service's Variables tab after deploy, but stashing it now saves a click.
- Click Deploy. Railway provisions the service, attaches the volume, and runs the Dockerfile build.
- Watch the build logs. The Rust workspace build with
lto = "fat"is the longest step. The healthcheck against/readywaits up to 300s. - When the build finishes and the healthcheck passes, your service is
live at
https://<service>.up.railway.app.
The template configures every variable below. Only RUSTY_RED_API_TOKENS
is pre-filled with a generated value; the rest can be edited before
deploy if you want to deviate from the defaults.
| Variable | Default | Edit before deploy if... |
|---|---|---|
RUSTY_RED_API_TOKENS |
${{secret(64, "abcdef0123456789")}}=graph:read|graph:write|context:read|admin:read|federation:write |
You want to seed with a token you control, or split into multiple scoped tokens. |
RUSTY_RED_REQUIRE_AUTH |
true |
You are deploying on a private network with another auth layer in front. |
RUSTY_RED_REQUIRE_VOLUME |
true |
Never. Keep on. |
RUSTY_RED_MODE |
embedded |
Never for the template. redis is legacy compatibility only. |
RUSTY_RED_DATA_DIR |
/app/data/rusty-red |
You change the volume mount path (do both together). |
RUSTY_RED_DURABILITY |
aof_everysec |
You need strict ACID — set to aof_always (and enable STRICT_ACID). |
RUSTY_RED_SNAPSHOT_INTERVAL_WRITES |
1000 |
You have a write-heavy workload and want smaller AOF replays. |
RUSTY_RED_KEY_PREFIX |
rusty-red:tenant |
You want a different namespace for your tenant keyspace. |
RUSTY_RED_SERVICE_NAME |
rusty-red-graph-database |
You want a different name in OpenAPI / .well-known/* metadata. |
RUSTY_RED_API_TITLE |
Rusty Red Graph Database API |
Same as above. |
RUSTY_RED_MCP_ENABLED |
true |
You don't want MCP exposed at all. |
RUSTY_RED_MCP_READ_ONLY |
true |
You want to expose write tools via MCP. Token still needs graph:write. |
RUSTY_RED_MCP_ALLOW_ADMIN |
false |
You want to expose the admin tool surface. Token still needs admin:read. |
/app/data/rusty-red/
├── snapshot.bin # most recent full snapshot
├── snapshot.bin.tmp # in-flight snapshot rotation (transient)
├── aof.log # append-only log of writes since last snapshot
└── (per-tenant data files keyed under RUSTY_RED_KEY_PREFIX)
The exact internal file layout is an implementation detail of
rustyred-core; do not rely on it from outside the service.
You get exactly what SECURITY.md calls the "default posture":
/,/search,/search.json,/crawl,/federate/submit,/v1/*,/mcp, and/metricsreject unauthenticated requests./health,/ready,/openapi.json,/.well-known/agent.json, and/.well-known/mcp/rustyred.jsonare intentionally open. They expose no tenant data.- MCP starts in read-only mode.
- The service refuses to start without the mounted volume.
If you want a different posture, edit the variables before deploy. Do
not flip RUSTY_RED_REQUIRE_AUTH=false on a service with a public
domain unless you have an external auth layer in front of it.
The 1 GiB default is enough for evaluation and small workloads. To grow:
- Railway service settings → Volume → resize.
- Restart the service.
- The volume mount path stays the same; existing data is preserved.
There is no online resize; expect a brief restart.
The Rust workspace builds with lto = "fat"; the resulting binary is
small. RAM is the constraint, not CPU. Plan for:
- Working-set size of your graph plus indexes (HNSW, BM25, H3).
- Snapshot headroom (Railway provides ample memory at most plan tiers).
- Per-tenant memory quotas via
RUSTY_RED_TENANT_MEMORY_QUOTA_BYTESif you run multi-tenant.
The template ships a single replica. rustyred-server is single-writer
with read snapshots; running multiple replicas behind a load balancer
would require coordination that the embedded mode does not implement.
Stay at one replica until you have a sharding strategy.
- Pause writes — either stop the service or block writes at the ingress layer.
- Use the Railway CLI or dashboard to copy the contents of
/app/data/rusty-red/out. Snapshots are self-contained; AOF replays the gap between snapshots. - Resume writes.
- Provision a new Railway service from the template.
- Stop it before first start.
- Copy your backed-up
snapshot.binandaof.loginto the new service's volume at/app/data/rusty-red/. - Start the service. On first start, RustyRed loads the snapshot and replays the AOF.
- Production: at least daily off-platform, plus before every upgrade.
- Evaluation: snapshot once before destructive testing.
- Pin to a tagged release in your service's Source settings (do not
track
main). - To upgrade, bump the pinned ref and redeploy.
- RustyRed runs
rustyred-upgrade-formatmigrations at startup if the on-disk format changed. No export/re-import. - Watch the logs for migration messages on first start.
Major upgrades are the only ones that may break the on-disk format guarantee. Read the release notes before deploying. Back up first.
Railway templates deploy directly from the upstream GitHub repository
by default ("upstream repo" model). You stay on the template's update
stream — when the template author pushes new commits to main, you can
opt into PR-style updates.
Eject when:
- You need to customize the Dockerfile,
railway.toml, or env-var defaults at the deploy level rather than overriding them per-service. - You want to add organization-specific build-time changes.
- You want to keep your own private fork outside the public template update channel.
Eject flow:
- Service settings → Source → Upstream Repo → Eject.
- Railway forks the template repo into your GitHub account.
- Your service now deploys from your fork.
The vendored proto at vendor/proto/rustyred/v1/rustyred.proto is
missing from the build context. This should not happen with the
shipped Dockerfile — it copies vendor/ explicitly. If it does:
- Check the commit you pinned has
vendor/proto/rustyred/v1/rustyred.proto. - Confirm
.railwayignoredoes not excludevendor/. - Check
vendor/proto/SOURCE_COMMITexists and is non-empty.
The service is up but the volume is not mounted, or RUSTY_RED_DATA_DIR
does not match the mount path. Check:
- Railway service Volumes tab shows the volume attached.
- Mount path matches
RUSTY_RED_DATA_DIRexactly (default/app/data/rusty-red). RAILWAY_VOLUME_MOUNT_PATHis set automatically by Railway when a volume is attached.
RUSTY_RED_API_TOKENS is empty or malformed. Check:
- The Variables tab shows a non-empty value.
- Format is
<secret>=<scope>|<scope>|..., comma-separated for multiple entries. - Your client sends
Authorization: Bearer <secret>.
RUSTY_RED_MCP_READ_ONLY=true (the default) hides write tools. To
expose them, set RUSTY_RED_MCP_READ_ONLY=false and ensure the calling
token has graph:write scope.
The default threshold is intentionally low. Tune
RUSTY_RED_SLOW_QUERY_NANOS if you want a stricter signal, and lift
RUSTY_RED_SLOW_QUERY_CAPACITY if the ring buffer wraps faster than
you can drain it.
This is the first signal of either a credential leak or a misconfigured
client. Rotate RUSTY_RED_API_TOKENS and restart. See SECURITY.md
for the rotation procedure.
- This document, then
SECURITY.md, then the README. - The Railway template marketplace page has a "Discuss this Template" button that routes to the template creator.
- Issues with the database itself (not Railway-specific) go to the repository's issue tracker.