Skip to content

Latest commit

 

History

History
241 lines (178 loc) · 9.75 KB

File metadata and controls

241 lines (178 loc) · 9.75 KB

Railway Template Operator Guide

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.

What this template gives you

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_TOKENS is pre-filled with a fresh 64-character hex secret generated by Railway's template variable function — the equivalent of openssl rand -hex 32 — scoped to graph:read|graph:write|context:read|admin:read|federation:write.
  • MCP exposed at /mcp in 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.

One-click deploy flow

  1. Click the Deploy on Railway badge in the repository README.
  2. Railway opens the template page. Confirm the listed variables. The pre-generated RUSTY_RED_API_TOKENS value will look like: <64-hex-chars>=graph:read|graph:write|context:read|admin:read|federation:write.
  3. 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.
  4. Click Deploy. Railway provisions the service, attaches the volume, and runs the Dockerfile build.
  5. Watch the build logs. The Rust workspace build with lto = "fat" is the longest step. The healthcheck against /ready waits up to 300s.
  6. When the build finishes and the healthcheck passes, your service is live at https://<service>.up.railway.app.

Variables the template prompts for

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.

Volume layout

/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.

Auth posture out of the box

You get exactly what SECURITY.md calls the "default posture":

  • /, /search, /search.json, /crawl, /federate/submit, /v1/*, /mcp, and /metrics reject unauthenticated requests.
  • /health, /ready, /openapi.json, /.well-known/agent.json, and /.well-known/mcp/rustyred.json are 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.

Scaling

Volume size

The 1 GiB default is enough for evaluation and small workloads. To grow:

  1. Railway service settings → Volume → resize.
  2. Restart the service.
  3. The volume mount path stays the same; existing data is preserved.

There is no online resize; expect a brief restart.

Memory and CPU

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_BYTES if you run multi-tenant.

Horizontal replicas

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.

Backup and restore

Backup

  1. Pause writes — either stop the service or block writes at the ingress layer.
  2. 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.
  3. Resume writes.

Restore

  1. Provision a new Railway service from the template.
  2. Stop it before first start.
  3. Copy your backed-up snapshot.bin and aof.log into the new service's volume at /app/data/rusty-red/.
  4. Start the service. On first start, RustyRed loads the snapshot and replays the AOF.

Backup frequency

  • Production: at least daily off-platform, plus before every upgrade.
  • Evaluation: snapshot once before destructive testing.

Upgrade path

Patch and minor upgrades

  1. Pin to a tagged release in your service's Source settings (do not track main).
  2. To upgrade, bump the pinned ref and redeploy.
  3. RustyRed runs rustyred-upgrade-format migrations at startup if the on-disk format changed. No export/re-import.
  4. Watch the logs for migration messages on first start.

Major upgrades (when they happen)

Major upgrades are the only ones that may break the on-disk format guarantee. Read the release notes before deploying. Back up first.

When to eject from the upstream template repo

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:

  1. Service settings → Source → Upstream Repo → Eject.
  2. Railway forks the template repo into your GitHub account.
  3. Your service now deploys from your fork.

Troubleshooting

Build fails with "Cannot find rustyred.proto"

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:

  1. Check the commit you pinned has vendor/proto/rustyred/v1/rustyred.proto.
  2. Confirm .railwayignore does not exclude vendor/.
  3. Check vendor/proto/SOURCE_COMMIT exists and is non-empty.

/ready returns 503

The service is up but the volume is not mounted, or RUSTY_RED_DATA_DIR does not match the mount path. Check:

  1. Railway service Volumes tab shows the volume attached.
  2. Mount path matches RUSTY_RED_DATA_DIR exactly (default /app/data/rusty-red).
  3. RAILWAY_VOLUME_MOUNT_PATH is set automatically by Railway when a volume is attached.

Service returns 401 on every /v1/* request

RUSTY_RED_API_TOKENS is empty or malformed. Check:

  1. The Variables tab shows a non-empty value.
  2. Format is <secret>=<scope>|<scope>|..., comma-separated for multiple entries.
  3. Your client sends Authorization: Bearer <secret>.

MCP write tool returns "tool not available"

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.

Slow queries piling up in /v1/diagnostics/slow_queries

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.

Auth-rejection rate spiking on /metrics

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.

Where to get help

  • 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.