Skip to main content

Saga Deployment Guide

This guide covers deploying Saga service discovery in various environments, from local development to production Kubernetes clusters.

Deployment Options

Saga can be deployed as:

  • Binary - Standalone executable
  • Docker Container - Containerized deployment
  • Kubernetes - Orchestrated container deployment
  • Systemd Service - Linux service management

Deployment Overview​

Best for: Development and testing

  • ✅ Quick setup
  • ✅ Easy debugging
  • ✅ Hot reload support
  • ❌ Not suitable for production

Local Development​

Using Makefile​

The simplest way to run Saga locally:

# Start Redis first
docker compose -f infra/compose.yml up -d redis

# Start Saga
REDIS_URL=redis://localhost:6379 PORT=8030 make saga-dev
Makefile Commands

The Makefile provides convenient commands:

  • make saga-dev - Development mode
  • make saga-watch - Watch mode with hot reload
  • make saga-test - Run tests
  • make saga-build - Build release binary
  • make saga-update - Update saga: rebuild, reinstall CLI, and restart service

Using Cargo​

For direct control:

cd shared/saga
REDIS_URL=redis://localhost:6379 PORT=8030 cargo run

Using Docker Compose​

Saga is included in the main Docker Compose file:

docker compose -f infra/compose.yml up saga
Docker Compose Benefits
  • Automatic dependency management
  • Network isolation
  • Volume management
  • Health checks

Docker Deployment​

Building Docker Image​

cd shared/saga
docker build -t saga:latest .

Build output:

[+] Building 45.2s
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 2.00kB
=> [1/8] FROM rust:1.83-slim
=> ...
=> => exporting to image
=> => exporting layers
=> => writing image sha256:...
=> => naming to docker.io/library/saga:latest

Running Container​

docker run -d \
--name saga \
-p 8030:8030 \
-e REDIS_URL=redis://redis:6379 \
-e PORT=8030 \
-e HOST=0.0.0.0 \
saga:latest

Docker Compose Configuration​

Saga is configured in infra/compose.yml:

saga:
build:
context: ..
dockerfile: shared/saga/Dockerfile
container_name: saga
ports:
- "8030:8030"
environment:
- REDIS_URL=redis://redis:6379
- PORT=8030
- HOST=0.0.0.0
- REGISTRATION_TTL=60
- HEARTBEAT_INTERVAL=30
depends_on:
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8030/api/v1/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s

Start with:

docker compose -f infra/compose.yml up -d saga

Production Deployment​

Automatic Server Setup​

Saga includes an automated setup script that detects server environments and automatically configures saga as a systemd service.

On a server environment, the setup script automatically:

  • ✅ Detects server environment and operating system
  • ✅ Creates appropriate service file (systemd on Linux, launchd on macOS)
  • ✅ Enables and starts the service
  • ✅ Configures automatic restart on failure
# Build saga first
cd shared/saga
cargo build --release

# Run setup script (automatically detects server and sets up service)
./scripts/setup.sh

# The script will:
# 1. Create configuration directory (~/.saga)
# 2. Create config.toml
# 3. Detect server environment and OS
# 4. Create and enable service automatically:
# - Linux: systemd service (/etc/systemd/system/saga.service)
# - macOS: launchd service (~/Library/LaunchAgents/com.saga.service.plist)

What gets detected:

  • ✅ Operating system (Linux or macOS)
  • ✅ Service manager available (systemctl on Linux, launchctl on macOS)
  • ✅ Not running in Docker container
  • ✅ Not running in CI environment
  • ✅ Can use sudo (Linux) or launchctl (macOS)

Manual control:

# Force systemd setup on Linux (even if not auto-detected)
./scripts/setup.sh --systemd

# Force launchd setup on macOS (even if not auto-detected)
./scripts/setup.sh --launchd

# Skip service setup (even on servers)
./scripts/setup.sh --no-service

# Non-interactive mode (auto-enables service)
./scripts/setup.sh --non-interactive --systemd # Linux
./scripts/setup.sh --non-interactive --launchd # macOS

Binary Deployment​

Build optimized release binary:

cd shared/saga
cargo build --release

Output location: target/release/saga

Binary size: ~5-10 MB (stripped)

Updating Saga​

When you make changes to saga and rebuild, you need to update the running service. Use the convenient Makefile target:

One command to update everything:

make saga-update

This command automatically:

  1. ✅ Builds the release binary (if not already built)
  2. ✅ Reinstalls the saga CLI globally
  3. ✅ Restarts the service if it's running (systemd/launchd)

What happens:

  • If saga is running as a systemd service → restarts the service
  • If saga is running as a launchd service → restarts the service
  • If saga is not installed as a service → just updates the CLI
  • CLI is always updated to the latest build

Verify the update:

# Check CLI version
saga --version

# Check service status (short command)
saga status

# Or using Makefile
make saga-service-status

Troubleshooting:

If the service doesn't restart after update:

  • Check service status: saga status or make saga-service-status
  • Check logs: make saga-service-logs
  • Manually restart: saga restart or make saga-service-restart

Background Service Setup​

The setup script automatically creates the appropriate service file based on your operating system:

Automatic setup:

./scripts/setup.sh  # Automatically detects Linux and sets up systemd

Manual creation: /etc/systemd/system/saga.service:

[Unit]
Description=Saga Service Discovery
Documentation=https://github.com/your-org/one/tree/main/shared/saga
After=network.target redis.service
Requires=redis.service

[Service]
Type=simple
User=saga
Group=saga
WorkingDirectory=/opt/saga
ExecStart=/usr/local/bin/saga
Restart=always
RestartSec=10

# Environment variables
Environment="REDIS_URL=redis://redis-cluster:6379"
Environment="PORT=8030"
Environment="HOST=0.0.0.0"
Environment="REGISTRATION_TTL=120"
Environment="HEARTBEAT_INTERVAL=30"
Environment="SERVICE_NAME=saga"

# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=saga

# Security
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/saga

[Install]
WantedBy=multi-user.target

Enable and start:

# If using automatic setup, service is already enabled/started
# Otherwise, manually enable and start:

# Reload systemd
sudo systemctl daemon-reload

# Enable service (start on boot)
sudo systemctl enable saga

# Start service
sudo systemctl start saga

# Check status
sudo systemctl status saga

# View logs
sudo journalctl -u saga -f
Automatic Setup Benefits

The automatic setup script (./scripts/setup.sh) handles all of this for you:

  • ✅ Detects operating system automatically (Linux or macOS)
  • ✅ Creates optimized service file from template (systemd or launchd)
  • ✅ Configures all environment variables
  • ✅ Sets up proper security settings
  • ✅ Enables automatic restart on failure
  • ✅ Starts the service immediately

Use automatic setup unless you need custom configuration.

Systemd Best Practices
  • Use dedicated user (saga) for security
  • Set RestartSec to prevent rapid restarts
  • Use ProtectSystem=strict for security
  • Configure log rotation for journal logs

Kubernetes Deployment​

Deployment Manifest​

Create k8s/saga-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
name: saga
labels:
app: saga
spec:
replicas: 2
selector:
matchLabels:
app: saga
template:
metadata:
labels:
app: saga
spec:
containers:
- name: saga
image: saga:latest
imagePullPolicy: Always
ports:
- containerPort: 8030
name: http
env:
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: saga-secrets
key: redis-url
- name: PORT
value: "8030"
- name: HOST
value: "0.0.0.0"
- name: REGISTRATION_TTL
value: "120"
- name: HEARTBEAT_INTERVAL
value: "30"
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /api/v1/health
port: 8030
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/v1/health
port: 8030
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3

Service Manifest​

Create k8s/saga-service.yaml:

apiVersion: v1
kind: Service
metadata:
name: saga
labels:
app: saga
spec:
type: ClusterIP
ports:
- port: 8030
targetPort: 8030
protocol: TCP
name: http
selector:
app: saga

Secrets​

Create k8s/saga-secrets.yaml:

apiVersion: v1
kind: Secret
metadata:
name: saga-secrets
type: Opaque
stringData:
redis-url: "redis://redis-cluster:6379"

Apply manifests:

kubectl apply -f k8s/saga-secrets.yaml
kubectl apply -f k8s/saga-deployment.yaml
kubectl apply -f k8s/saga-service.yaml
Kubernetes Features
  • Replicas: Run multiple instances for high availability
  • Health Probes: Automatic restart on failure
  • Resource Limits: Prevent resource exhaustion
  • Service Discovery: Automatic DNS resolution

Health Checks​

Saga provides a health check endpoint for monitoring and orchestration.

Health Check Endpoint​

curl http://localhost:8030/api/v1/health

Response:

{
"status": "healthy",
"service": "saga",
"version": "0.8.1",
"redis": "connected",
"cache": {
"size": 5,
"hits": 142,
"misses": 18,
"hit_ratio": 0.8875,
"last_refresh": "2025-12-22T14:29:53.944455Z"
}
}

Using Health Checks​

healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8030/api/v1/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s

Monitoring​

Metrics​

Saga exposes cache metrics in the health endpoint:

{
"cache": {
"size": 5, // Number of services cached
"hits": 142, // Cache hits (fast lookups)
"misses": 18, // Cache misses (Redis queries)
"hit_ratio": 0.8875, // Efficiency (hits / total)
"last_refresh": "..." // Last cache refresh time
}
}

Interpreting Metrics:

  • Hit ratio > 0.8: Excellent cache performance
  • Hit ratio 0.5-0.8: Good cache performance
  • Hit ratio < 0.5: Consider increasing cache refresh frequency

Logging​

Saga uses structured logging with tracing. Configure log level:

# Error level only
RUST_LOG=saga=error cargo run

# Warning and above
RUST_LOG=saga=warn cargo run

# Info level (default)
RUST_LOG=saga=info cargo run

# Debug level
RUST_LOG=saga=debug cargo run

# Trace level (very verbose)
RUST_LOG=saga=trace cargo run

Available log levels:

  • error - Errors only
  • warn - Warnings and errors
  • info - Informational messages (default)
  • debug - Debug information
  • trace - Very verbose logging

Scaling​

Horizontal Scaling​

Saga can be scaled horizontally:

Benefits:

  • ✅ High availability
  • ✅ Load distribution
  • ✅ Fault tolerance

Configuration:

  1. Deploy multiple Saga instances
  2. All instances connect to the same Redis
  3. Service registrations are shared via Redis
  4. Use load balancer to distribute requests

Example:

# Kubernetes deployment with 3 replicas
replicas: 3

Redis Scaling​

For high availability Redis setups:

REDIS_URL=redis-sentinel://sentinel1:26379,sentinel2:26379/mymaster

Features:

  • Automatic failover
  • High availability
  • No single point of failure

Security Considerations​

Network Security​

# Allow only internal network access
ufw allow from 10.0.0.0/8 to any port 8030

# Or use iptables
iptables -A INPUT -p tcp --dport 8030 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 8030 -j DROP

Authentication​

Future Feature

Currently, Saga API does not require authentication. Future versions will support:

  • API key authentication
  • OAuth2/JWT tokens
  • Role-based access control
  • Rate limiting

For now, use network policies:

  • Restrict access to internal networks
  • Use firewall rules
  • Deploy behind reverse proxy with authentication

Backup and Recovery​

Redis Backup​

Saga relies on Redis for storage. Backup Redis regularly:

# Manual backup
redis-cli BGSAVE

# Backup file location
# Linux: /var/lib/redis/dump.rdb
# Docker: Volume mount point

Recovery Process​

If Redis data is lost:

  1. Restore Redis from backup:

    # Stop Redis
    systemctl stop redis

    # Restore backup
    cp /backups/redis/dump_20251222.rdb /var/lib/redis/dump.rdb

    # Start Redis
    systemctl start redis
  2. Restart Saga service:

    systemctl restart saga
  3. Services re-register:

    • Services will need to re-register (or use registration scripts)
    • Implement automatic re-registration on startup
Recovery Best Practices
  • Backup Redis daily
  • Test restore procedures regularly
  • Implement automatic service re-registration
  • Monitor Redis health continuously

Troubleshooting Deployment​

Service Won't Start​

# Docker
docker logs saga

# Systemd
journalctl -u saga -n 50

# Direct
cargo run 2>&1 | tee saga.log

High Memory Usage​

# Check cache size
curl http://localhost:8030/api/v1/health | jq .cache.size

# If cache is too large, consider:
# - Reducing cache refresh interval
# - Clearing unused services

Next Steps​