Get a Basic Memory instance running on Railway with a persistent volume, accessible via HTTP (SSE transport on port 8000). No sync yet — just a working remote instance.
- Railway account
- Docker (for local testing before deploying)
- The Basic Memory repo cloned locally
- Task 1: Build Docker image locally (must use
--platform linux/amd64on Apple Silicon) - Task 2: Run container with sidecar env vars
- Task 3: Verify container starts (check logs for startup sequence)
- Task 4: Verify
basic-memory --versionworks inside container (v0.18.5) - Task 5: Verify
basic-memory statusshows a project with no errors - Task 6: Write a test note inside the container (CLI flag is
--foldernot--directory) - Task 7: Read the test note back to confirm persistence
- Task 8: Verify SSE endpoint responds on port 8000 (path is
/mcpnot/ssein FastMCP 3.x) - Task 9: Clean up local container and volumes
- Task 10: Create
docker-compose.sidecar.ymlwith sidecar-specific config - Task 11: Test docker compose up with the sidecar compose file
- Task 12: Verify sidecar compose works end-to-end (write/read/SSE all work)
- Task 13: Create
Dockerfile.sidecar(runs as root for Railway volume permissions, entrypoint.sh for dir creation) - Task 14: Link Railway project and service (
exemplary-renewal/bm-sync) - Task 15: Set env vars via
railway variables --set - Task 16: Deploy via
railway up --detach - Task 17: Create persistent volume at
/app/data(50GB) - Task 18: Assign public domain (
robust-creation-production-70db.up.railway.app) - Task 19: Verify SSE endpoint returns HTTP 200 with
text/event-stream - Task 20: Commit all changes
- Project: exemplary-renewal
- Service: bm-sync (formerly robust-creation)
- URL: https://robust-creation-production-70db.up.railway.app
- MCP Endpoint: https://robust-creation-production-70db.up.railway.app/mcp
- Volume: 50GB at
/app/data - Dockerfile:
Dockerfile.sidecar
The existing Dockerfile works. Test it before deploying.
# Build locally
docker build -t basic-memory-sidecar .
# Run locally
docker run -d \
--name bm-sidecar \
-p 8000:8000 \
-v bm-data:/app/data/basic-memory \
-v bm-config:/app/.basic-memory \
-e BASIC_MEMORY_DEFAULT_PROJECT=shared \
-e BASIC_MEMORY_SYNC_CHANGES=true \
-e BASIC_MEMORY_SYNC_DELAY=1000 \
-e BASIC_MEMORY_LOG_LEVEL=INFO \
basic-memory-sidecarbasic-memory mcp --transport sse --host 0.0.0.0 --port 8000runsMcpContainer.create()reads/creates config at/app/.basic-memory/config.json- If no projects exist, auto-creates "main" project at
BASIC_MEMORY_HOME(default~/basic-memory, but Dockerfile creates/app/data/basic-memory) - Database auto-created at
/app/.basic-memory/memory.db - Alembic migrations run automatically
- SyncCoordinator starts file watcher in background
- SSE server listens on port 8000
# Check container is running
docker logs bm-sidecar
# Test MCP endpoint exists (SSE transport)
# The SSE endpoint won't respond to plain curl like a REST API,
# but you can check the container is healthy:
docker exec bm-sidecar basic-memory --version
docker exec bm-sidecar basic-memory statusIf you have an MCP client that supports SSE transport, connect to http://localhost:8000/sse and call list_memory_projects().
docker stop bm-sidecar && docker rm bm-sidecar
docker volume rm bm-data bm-config-
Push to a container registry (GitHub Container Registry, Docker Hub, etc.):
docker tag basic-memory-sidecar ghcr.io/<your-org>/basic-memory-sidecar:latest docker push ghcr.io/<your-org>/basic-memory-sidecar:latest
-
In Railway:
- Create new project
- Add service → Docker Image →
ghcr.io/<your-org>/basic-memory-sidecar:latest
- In Railway:
- Create new project
- Add service → GitHub Repo → select the basic-memory fork/repo
- Railway will detect the Dockerfile and build automatically
Environment Variables (set in Railway dashboard):
BASIC_MEMORY_HOME=/app/data/shared
BASIC_MEMORY_DEFAULT_PROJECT=main
BASIC_MEMORY_CONFIG_DIR=/app/data/.config
BASIC_MEMORY_SYNC_CHANGES=true
BASIC_MEMORY_SYNC_DELAY=1000
BASIC_MEMORY_LOG_LEVEL=INFO
Volume (create in Railway dashboard):
- Mount path:
/app/data - This single volume persists markdown files, config, and SQLite DB
- Config stored at
/app/data/.configviaBASIC_MEMORY_CONFIG_DIR
Port: Railway auto-detects port 8000 from the Dockerfile EXPOSE directive.
Health Check: The Dockerfile includes a health check (basic-memory --version). Railway will use this.
# Check Railway logs for startup sequence:
# - "Starting MCP server"
# - "Database initialized"
# - "SyncCoordinator started"
# - No errors
# If Railway provides a public URL, test connectivity:
# (The SSE endpoint is at /sse on the Railway URL)
curl -N https://<railway-url>/sse
# Should return SSE stream headers (content-type: text/event-stream)The container auto-creates a "main" project. We want it named "shared" instead.
SSH into Railway or use railway run:
# Check current state
basic-memory project list
# If "main" exists but we want "shared":
basic-memory project add shared /app/data/shared
basic-memory project default shared
# Create the directory structure
mkdir -p /app/data/shared/notes
mkdir -p /app/data/shared/users
mkdir -p /app/data/shared/conversations
mkdir -p /app/data/shared/decisionsOr let the BASIC_MEMORY_DEFAULT_PROJECT=shared env var handle it — the auto-created project will be named "shared" if BASIC_MEMORY_HOME=/app/data/shared.
Add this env var:
BASIC_MEMORY_HOME=/app/data/shared
From inside the container (or via MCP client):
# Via CLI
basic-memory tool write-note \
--title "Deployment Test" \
--content "- [test] Sidecar deployment verified #deployment" \
--directory "notes"
# Verify
basic-memory tool read-note --identifier "Deployment Test"
basic-memory status- Docker image builds locally without errors
- Container starts, logs show successful initialization
-
basic-memory statusshows project with 0 sync errors - Can write and read a note via CLI inside the container
- Railway deployment starts successfully (check logs)
- Railway persistent volume is mounted at
/app/data - Config stored in volume (not lost on redeploy)
- Project "shared" exists with correct directory structure
- No sync with local machines (Phase 01)
- No rclone or R2 setup (Phase 01)
- No chat app integration (Phase 02)
- No cron jobs or automation (Phase 01)
Container exits immediately: Check logs for Python import errors. Ensure uv sync --locked completed during build.
"No project found" errors: Set BASIC_MEMORY_HOME and BASIC_MEMORY_DEFAULT_PROJECT env vars. The auto-bootstrap creates a project from BASIC_MEMORY_HOME.
Database errors on startup: Usually means the volume isn't mounted. Check that /app/data persists. If DB is corrupted, delete it — bm reset rebuilds from files.
Port not reachable on Railway: Ensure Railway sees the EXPOSE 8000 in the Dockerfile. Check the service's networking settings in Railway dashboard.
- Apple Silicon requires
--platform linux/amd64: Thesqlite_vecbinary is x86-only. Build withdocker build --platform linux/amd64or setplatform: linux/amd64in compose. - Config lives at
~appuser/.basic-memory/: The Dockerfile creates/app/.basic-memory/but the appuser's home is/home/appuser/. UseBASIC_MEMORY_CONFIG_DIR=/app/data/.configto put config in the persistent volume. - Single volume is best: Put config, DB, and data in one volume at
/app/data. Avoids permission issues and Railway's one-volume-per-service limit. - SSE path is
/mcpnot/sse: FastMCP 3.x changed the endpoint path. - CLI write-note uses
--foldernot--directory: The MCP tool parameter isdirectorybut the CLI flag is--folder. BASIC_MEMORY_DEFAULT_PROJECTdoesn't rename auto-created project: The auto-bootstrap always creates a project named "main". The env var just sets which project is default.- Semantic search disabled for sidecar:
sqlite_vechas ELF compatibility issues under emulation. SetBASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=falsefor local testing. Railway runs native amd64 so this can be re-enabled there. - Railway volumes mount as root: The
USER appuserdirective conflicts with Railway volumes.Dockerfile.sidecarruns as root with an entrypoint that creates dirs. PORT=8000env var required: Railway needs explicit PORT to route traffic to the container.RAILWAY_DOCKERFILE_PATH=Dockerfile.sidecar: Tells Railway to use the sidecar Dockerfile instead of the default one.
The Dockerfile, startup sequence, and auto-initialization are well-understood from code inspection. The only uncertainty is Railway-specific volume configuration (mount path behavior, single vs multiple volumes), which may need minor adjustments during deploy. If Railway behaves differently than expected, stop and investigate before proceeding.