Railway Deployment
This page provides step-by-step instructions for deploying AutoMem on Railway, including service configuration, internal networking setup, and troubleshooting. Railway is a Platform-as-a-Service (PaaS) that provides containerized deployments with persistent volumes and internal service discovery.
For local development setup, see the Development Guide. For backup configuration and monitoring, see Backup & Recovery and Health Monitoring. For MCP bridge setup specifically, see the MCP Integration guide.
Overview
Section titled “Overview”Railway deployment provisions three services: the AutoMem Flask API (memory-service), FalkorDB graph database (falkordb), and optionally the MCP bridge for cloud AI platforms (mcp-sse-server). Railway handles container orchestration, internal networking via IPv6, persistent volumes, and health checks.
Production Deployment Architecture
Section titled “Production Deployment Architecture”graph TB
subgraph internet["External Clients"]
DirectAPI["Direct API Clients<br/>curl, Python, MCP package"]
CloudAI["Cloud AI Platforms<br/>ChatGPT, Claude.ai, ElevenLabs"]
LocalTools["Local Development Tools<br/>Cursor, Claude Desktop"]
end
subgraph railway["Railway Project"]
subgraph public["Public Layer"]
Domain1["automem.up.railway.app<br/>HTTPS/443"]
Domain2["mcp.up.railway.app<br/>HTTPS/443"]
LB["Railway Load Balancer<br/>SSL Termination"]
end
subgraph services["Service Layer"]
subgraph api["memory-service"]
FlaskApp["app.py<br/>Flask + Gunicorn<br/>PORT=8001"]
Workers["Background Workers<br/>enrichment_worker<br/>embedding_worker<br/>consolidation_worker<br/>sync_worker"]
end
subgraph mcp["mcp-sse-server"]
MCPBridge["server.js<br/>Express + SSE<br/>PORT=8080"]
end
subgraph db["falkordb"]
FalkorDB["FalkorDB Container<br/>Port 6379<br/>FALKOR_PASSWORD"]
Volume["Persistent Volume<br/>/var/lib/falkordb/data<br/>50GB"]
end
end
subgraph networking["Internal Network"]
DNS["*.railway.internal<br/>IPv6 DNS<br/>RAILWAY_PRIVATE_DOMAIN"]
end
subgraph external["External Services"]
Qdrant["Qdrant Cloud<br/>QDRANT_URL<br/>QDRANT_API_KEY"]
OpenAI["OpenAI API<br/>OPENAI_API_KEY"]
Voyage["Voyage AI<br/>VOYAGE_API_KEY"]
S3["S3 Bucket<br/>Backups"]
end
end
DirectAPI -->|HTTPS| Domain1
CloudAI -->|HTTPS/SSE| Domain2
LocalTools -->|HTTPS| Domain1
LB --> Domain1
LB --> Domain2
Domain1 --> FlaskApp
Domain2 --> MCPBridge
MCPBridge -->|"http://memory-service.railway.internal:8001<br/>AUTOMEM_API_URL"| FlaskApp
FlaskApp -->|"redis://falkordb.railway.internal:6379<br/>FALKORDB_HOST"| FalkorDB
FlaskApp -->|HTTPS| Qdrant
FlaskApp -->|HTTPS| OpenAI
FlaskApp -->|HTTPS| Voyage
Workers --> FalkorDB
Workers --> Qdrant
FalkorDB --> Volume
DNS -.->|Resolves| FlaskApp
DNS -.->|Resolves| MCPBridge
DNS -.->|Resolves| FalkorDB
Quick Start: One-Click Template
Section titled “Quick Start: One-Click Template”The Railway template automates service provisioning with pre-configured environment variables and networking.
What Gets Deployed
Section titled “What Gets Deployed”| Service | Image/Source | Purpose | Public Domain |
|---|---|---|---|
memory-service | GitHub repo root | Flask API, background workers | Yes (required) |
falkordb | falkordb/falkordb:latest | Graph database with persistence | No (internal only) |
mcp-sse-server | GitHub repo mcp-sse-server/ | MCP bridge for cloud AI platforms | Yes (if using ChatGPT/Claude.ai) |
Template Variables (Auto-Generated)
Section titled “Template Variables (Auto-Generated)”The template uses Railway’s shared variables feature to generate secrets once and reference them across services. After template deployment, add your API keys:
- Navigate to Railway Dashboard →
memory-service→ Variables - Add required variables:
OPENAI_API_KEY— For embeddings and classificationQDRANT_URL— Qdrant Cloud endpoint (optional but recommended)QDRANT_API_KEY— Qdrant authentication
- Redeploy
memory-serviceto apply changes
Manual Deployment
Section titled “Manual Deployment”For production deployments or custom configurations, manual setup provides more control.
Step 1: FalkorDB Service
Section titled “Step 1: FalkorDB Service”FalkorDB requires persistent volume configuration to survive restarts.
Required Environment Variables
| Variable | Value | Purpose |
|---|---|---|
PORT | 6379 | Redis protocol port |
FALKOR_PASSWORD | <generated-secret> | Authentication password |
REDIS_ARGS | --save 60 1 --appendonly yes --appendfsync everysec | Persistence configuration |
Volume Configuration
- Mount Path:
/var/lib/falkordb/data(not/data) - Minimum Size: 1GB (adjust based on expected data)
- Purpose: Persists graph data across container restarts
Step 2: Memory Service
Section titled “Step 2: Memory Service”The memory-service runs the Flask API, background workers, and handles all memory operations.
Build Configuration
Builder: DOCKERFILEDockerfile Path: ./DockerfileRoot Directory: (empty - use repo root)Variable Reference Patterns
Railway supports two patterns for environment variables:
| Pattern | Example | Use Case | Stability |
|---|---|---|---|
| Hardcoded | FALKORDB_HOST=falkordb.railway.internal | Production, manual setup | Stable, easier to debug |
| Reference | FALKORDB_HOST=${{FalkorDB.RAILWAY_PRIVATE_DOMAIN}} | Templates only | Updates automatically, harder to troubleshoot |
Health Check Configuration
- Path:
/health - Timeout: 100 seconds
- Expected Response:
{"status": "healthy"}
Step 3: MCP Bridge (Optional)
Section titled “Step 3: MCP Bridge (Optional)”The mcp-sse-server service is only needed for cloud AI platforms (ChatGPT, Claude.ai, ElevenLabs). Skip this if you only use Cursor, Claude Desktop, or direct API access.
Build Configuration
Builder: DOCKERFILEDockerfile Path: mcp-sse-server/DockerfileRoot Directory: mcp-sse-serverEnvironment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
PORT | Yes | 8080 | HTTP server port |
AUTOMEM_API_URL | Yes | None | Internal URL: http://memory-service.railway.internal:8001 |
AUTOMEM_API_TOKEN | Yes | None | Copy from memory-service |
Networking Architecture
Section titled “Networking Architecture”Railway uses IPv6-based internal networking with automatic DNS resolution.
Health Check Sequence
Section titled “Health Check Sequence”sequenceDiagram
participant Client
participant API as "app.py<br/>/health"
participant Falkor as "FalkorDB<br/>redis_ping()"
participant Qdrant as "Qdrant<br/>client.get_collections()"
Client->>API: GET /health
API->>Falkor: Test connection<br/>redis_ping()
alt FalkorDB Available
Falkor-->>API: PONG
API->>API: Set falkordb: connected
else FalkorDB Unavailable
Falkor-->>API: Exception
API->>API: Set falkordb: unavailable
API-->>Client: 503 Service Unavailable
end
API->>Qdrant: Test connection<br/>get_collections()
alt Qdrant Available
Qdrant-->>API: Collections list
API->>API: Set qdrant: connected
else Qdrant Unavailable
Qdrant-->>API: Exception
API->>API: Set qdrant: unavailable<br/>(continue anyway)
end
API->>Falkor: Count memories<br/>MATCH (m:Memory) RETURN count(m)
Falkor-->>API: count
API->>API: Check enrichment queue<br/>ServiceState.enrichment_queue
API-->>Client: 200 OK<br/>{status, falkordb, qdrant,<br/>memory_count, enrichment}
IPv6 Dual-Stack Binding
Section titled “IPv6 Dual-Stack Binding”Railway’s internal networking uses IPv6 addresses. Services must bind to :: (dual-stack) instead of 0.0.0.0 (IPv4 only). AutoMem v0.7.1+ handles this automatically — Flask binds to host="::" at startup.
If you see this in startup logs, the service is correctly bound:
* Running on http://[::]:8001If you see the following (old versions only), upgrade to v0.7.1+:
* Running on http://0.0.0.0:8001Internal Service Discovery
Section titled “Internal Service Discovery”Services communicate using internal hostnames:
| Service Name | Internal Hostname | Port | Usage |
|---|---|---|---|
memory-service | memory-service.railway.internal | 8001 | MCP bridge connects here |
falkordb | falkordb.railway.internal | 6379 | Memory service connects here |
The internal hostname is automatically set by Railway as <service-name>.railway.internal.
TCP Proxy for External Access
Section titled “TCP Proxy for External Access”TCP Proxy enables external services (like GitHub Actions) to access internal Railway services.
When TCP Proxy Is Required
| Use Case | Needs TCP Proxy? | Reason |
|---|---|---|
| Internal service-to-service | No | Use .railway.internal hostnames |
| GitHub Actions backups | Yes | External runners can’t access internal network |
| Local development testing | Yes | External connection to Railway database |
| Public API access | No | Use public domains |
Configuration Steps
- Railway Dashboard →
falkordbservice - Settings → Networking → Enable TCP Proxy
- Note the generated endpoint:
RAILWAY_TCP_PROXY_DOMAIN: e.g.,monorail.proxy.rlwy.netRAILWAY_TCP_PROXY_PORT: e.g.,12345(random high port)
Usage in GitHub Actions
For GitHub Actions backups, set these secrets:
FALKORDB_HOST = monorail.proxy.rlwy.net # TCP Proxy domainFALKORDB_PORT = 12345 # TCP Proxy port (not 6379!)FALKORDB_PASSWORD = <same-as-railway>The backup workflow validates connectivity before running and checks that FALKORDB_HOST is not a *.railway.internal hostname (which external runners cannot reach).
Post-Deployment Verification
Section titled “Post-Deployment Verification”After deploying all services, verify connectivity and data flow.
Health Check
curl https://your-project.up.railway.app/healthExpected response:
{ "status": "healthy", "falkordb": "connected", "qdrant": "connected", "memory_count": 0, "enrichment": { "status": "running", "queue_depth": 0 }}Store Test Memory
curl -X POST https://your-project.up.railway.app/memory \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"content": "Test memory for deployment verification"}'Verify Background Workers
Check enrichment queue processing via /health — the processed count should increase after storing memories.
Verification Checklist
- Health endpoint returns
"status": "healthy" - FalkorDB shows
"connected" - Qdrant shows
"connected"(if configured) - Test memory stores successfully
- Enrichment worker processes memories (
processedcount increases) - MCP bridge responds on
/health(if deployed)
Configuration Test Patterns
Section titled “Configuration Test Patterns”graph TB
subgraph "Unit Tests (make test)"
UnitConfig["No external services<br/>Mock configuration<br/>Fast execution"]
MockFalkor["DummyGraph fixture<br/>In-memory stubs"]
MockQdrant["Mock Qdrant client<br/>No network calls"]
MockProvider["Mock embedding provider<br/>Deterministic outputs"]
end
subgraph "Integration Tests (make test-integration)"
IntConfig["Real services via Docker<br/>Actual configuration<br/>Comprehensive validation"]
DockerServices["docker-compose up<br/>FalkorDB + Qdrant"]
RealConfig["Load from .env<br/>or environment"]
TestAPICalls["HTTP requests to API<br/>AUTOMEM_TEST_BASE_URL"]
end
subgraph "Live Tests (make test-live)"
LiveConfig["Railway deployment<br/>Production-like environment<br/>Safety checks"]
ConfirmPrompt["Interactive confirmation<br/>AUTOMEM_ALLOW_LIVE=1"]
RailwayAPI["https://automem.up.railway.app"]
CleanupTests["Unique UUIDs<br/>Self-cleaning"]
end
UnitConfig --> MockFalkor
UnitConfig --> MockQdrant
UnitConfig --> MockProvider
IntConfig --> DockerServices
DockerServices --> RealConfig
RealConfig --> TestAPICalls
LiveConfig --> ConfirmPrompt
ConfirmPrompt --> RailwayAPI
RailwayAPI --> CleanupTests
Environment Variables Reference
Section titled “Environment Variables Reference”memory-service Variables
Section titled “memory-service Variables”| Variable | Required | Default | Description |
|---|---|---|---|
PORT | Yes (Railway) | 5000 | Flask listen port. Must be 8001 on Railway |
FALKORDB_HOST | Yes | localhost | FalkorDB hostname. Use falkordb.railway.internal on Railway |
FALKORDB_PORT | Yes | 6379 | FalkorDB port |
FALKORDB_PASSWORD | Yes | None | FalkorDB authentication password |
FALKORDB_GRAPH | No | memories | Graph name in FalkorDB |
AUTOMEM_API_TOKEN | Yes | None | API authentication token |
ADMIN_API_TOKEN | Yes | None | Admin endpoint authentication |
OPENAI_API_KEY | Recommended | None | Required for embeddings and classification |
QDRANT_URL | Recommended | None | Qdrant Cloud endpoint |
QDRANT_API_KEY | Recommended | None | Qdrant authentication |
QDRANT_COLLECTION | No | memories | Qdrant collection name |
falkordb Variables
Section titled “falkordb Variables”| Variable | Required | Default | Description |
|---|---|---|---|
PORT | Yes | 6379 | Redis protocol port |
FALKOR_PASSWORD | Yes | None | Database authentication |
REDIS_ARGS | Recommended | None | Persistence config: --save 60 1 --appendonly yes --appendfsync everysec |
mcp-sse-server Variables
Section titled “mcp-sse-server Variables”| Variable | Required | Default | Description |
|---|---|---|---|
PORT | Yes | 8080 | HTTP server port |
AUTOMEM_API_URL | Yes | None | Internal URL: http://memory-service.railway.internal:8001 |
AUTOMEM_API_TOKEN | Yes | None | Copy from memory-service |
Troubleshooting
Section titled “Troubleshooting”ECONNREFUSED on Port 8001
Section titled “ECONNREFUSED on Port 8001”Symptoms
Error: connect ECONNREFUSED fd12:ca03:42be:0:1000:50:1079:5b6c:8001MCP bridge or other services cannot connect to memory-service.
Solution 1: Add PORT Variable
Most common cause. Flask defaults to port 5000 without explicit PORT variable. Add PORT=8001 to memory-service variables in Railway Dashboard, then redeploy.
Solution 2: Update IPv6 Binding
If using older AutoMem versions (before v0.7.1), the service bound only to IPv4. Update to AutoMem v0.7.1 or later — Flask now binds to host="::" for dual-stack support.
Solution 3: Verify Internal Hostname
Ensure the MCP bridge uses AUTOMEM_API_URL=http://memory-service.railway.internal:8001 (internal hostname, not the public domain).
Variable Reference Not Resolving
Section titled “Variable Reference Not Resolving”Symptoms
Logs show literal ${{...}} strings instead of resolved values.
Cause
Railway variable references only work in templates, not manual service configuration.
Solution
Replace variable references with hardcoded values:
# Instead of:FALKORDB_HOST=${{FalkorDB.RAILWAY_PRIVATE_DOMAIN}}
# Use:FALKORDB_HOST=falkordb.railway.internalFalkorDB Data Loss After Restart
Section titled “FalkorDB Data Loss After Restart”Symptoms
All memories disappear after FalkorDB service restarts. /health shows memory_count: 0.
Cause
No persistent volume configured on FalkorDB service.
Solution
- Add Volume (prevents future data loss)
- Railway Dashboard →
falkordbservice - Settings → Volumes → Add Volume
- Mount Path:
/var/lib/falkordb/data - Redeploy service
- Railway Dashboard →
- Recover Existing Data (if Qdrant is intact)
- Run
scripts/recover_from_qdrant.py— see Backup & Recovery
- Run
GitHub Actions Backup Fails
Section titled “GitHub Actions Backup Fails”Symptoms
Error: Connection reset by peer (error 104)Backup workflow cannot connect to FalkorDB.
Cause
TCP Proxy not enabled or incorrect secrets configuration.
Solution
- Enable TCP Proxy (if not already enabled)
- Railway Dashboard →
falkordbservice - Settings → Networking → Enable TCP Proxy
- Note the public endpoint
- Railway Dashboard →
- Update GitHub Secrets
- Go to GitHub repo → Settings → Secrets and variables → Actions
- Set these values:
FALKORDB_HOST = monorail.proxy.rlwy.net # TCP Proxy domainFALKORDB_PORT = 12345 # TCP Proxy port (not 6379!)FALKORDB_PASSWORD = <same-as-railway>
- Verify Connectivity — the backup workflow includes pre-flight checks that validate the TCP proxy connection before attempting backup
Cost Optimization
Section titled “Cost Optimization”Railway uses usage-based pricing with resource limits.
Service Sizing Recommendations
| Service | RAM | vCPU | Storage | Est. Cost/Month |
|---|---|---|---|---|
memory-service | 512MB | 0.5 | - | ~$5 |
falkordb | 1GB | 1.0 | 2GB volume | ~$10 |
mcp-sse-server | 256MB | 0.25 | - | ~$2-3 |
Total Estimated Cost: $17-18/month (Railway Pro plan required: $20/month base)
Cost-Saving Options
- Remove
mcp-sse-serverif only using Cursor/Claude Desktop- Saves ~$2-3/month
- No impact on core API functionality
- Delete service from Railway Dashboard
- Use Qdrant Cloud Free Tier
- 1GB storage free
- Upgrade to $25/month for 10GB if needed
- Start with Smaller Volumes
- FalkorDB: 1GB initially (expand as needed)
- Volume resizing is non-disruptive
Security Best Practices
Section titled “Security Best Practices”- Always set
FALKOR_PASSWORD- Railway auto-generates this in templates
- For manual setup, use a strong random value
- Use Internal Networking
- Service-to-service communication via
.railway.internalhostnames - Never expose FalkorDB publicly (no public domain)
- Service-to-service communication via
- Rotate API Tokens Periodically
- Update
AUTOMEM_API_TOKENandADMIN_API_TOKENvia Railway Dashboard - Redeploy affected services
- Update
- Disable Public Domains on FalkorDB
- Only
memory-serviceandmcp-sse-serverneed public access - FalkorDB should only be accessible via internal network or TCP Proxy
- Only
- Service Naming Stability
- Internal DNS is based on service name (
<name>.railway.internal) - Renaming a service updates its
RAILWAY_PRIVATE_DOMAIN - Update hardcoded hostnames in other services after renaming
- Internal DNS is based on service name (
Next Steps
Section titled “Next Steps”After successful deployment:
- Configure automated backups — see Backup & Recovery
- Set up health monitoring — see Health Monitoring
- Test disaster recovery procedures — see Backup & Recovery
- Review environment configuration — see the Configuration Reference