Skip to main content

Overview

This guide covers deploying Beacon in production environments, including cloud platforms, containerization, scaling strategies, monitoring, and security hardening.

Deployment Options

Docker Deployment

Beacon includes a multi-stage Dockerfile for optimized production builds. Dockerfile (Dockerfile:1-27):
# ── Stage 1: Build ─────────────────────────────────────────────────────────
FROM rust:slim AS builder

WORKDIR /app

# Cache dependencies first
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs
RUN cargo build --release
RUN rm -rf src

# Build the real binary
COPY src ./src
RUN touch src/main.rs && cargo build --release

# ── Stage 2: Runtime ───────────────────────────────────────────────────────
FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/target/release/beacon /usr/local/bin/beacon

EXPOSE 8080

CMD ["beacon", "serve", "--port", "8080"]
Build and Run:
# Build the image
docker build -t beacon:latest .

# Run with environment variables
docker run -d \
  --name beacon-api \
  -p 8080:8080 \
  -e REDIS_URL=redis://redis:6379 \
  -e GEMINI_API_KEY=your_key \
  -e RUST_LOG=info \
  beacon:latest

# Run with env file
docker run -d \
  --name beacon-api \
  -p 8080:8080 \
  --env-file .env.production \
  beacon:latest
Image Size Optimization:
  • Multi-stage build reduces image size from ~1.5GB to ~100MB
  • Only includes runtime dependencies (ca-certificates)
  • Strips debug symbols and build artifacts

Docker Compose

Deploy Beacon with Redis using Docker Compose:
docker-compose.yml
version: '3.8'

services:
  beacon:
    build: .
    ports:
      - "8080:8080"
    environment:
      - REDIS_URL=redis://redis:6379
      - GEMINI_API_KEY=${GEMINI_API_KEY}
      - RUST_LOG=info
      - SUPABASE_URL=${SUPABASE_URL}
      - SUPABASE_SERVICE_KEY=${SUPABASE_SERVICE_KEY}
      - BEACON_WALLET_BASE=${BEACON_WALLET_BASE}
      - BEACON_WALLET_SOLANA=${BEACON_WALLET_SOLANA}
      - PAYMENT_AMOUNT_USDC=0.09
    depends_on:
      - redis
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru
    volumes:
      - redis-data:/data
    ports:
      - "6379:6379"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3

volumes:
  redis-data:
Usage:
# Start services
docker-compose up -d

# View logs
docker-compose logs -f beacon

# Scale Beacon instances
docker-compose up -d --scale beacon=3

# Stop services
docker-compose down

Render.com Deployment

Beacon includes a Render deployment configuration:
render.yaml
services:
  - type: web
    name: beacon
    runtime: docker
    plan: free
    envVars:
      - key: GEMINI_API_KEY
        sync: false
      - key: RUST_LOG
        value: info
    healthCheckPath: /health
    dockerfilePath: ./Dockerfile
Deploy Steps:
  1. Connect your GitHub repository to Render
  2. Create a new Web Service
  3. Select “Docker” as the environment
  4. Add environment variables in Render dashboard
  5. Deploy
Required Environment Variables:
REDIS_URL=<from Render Redis service>
GEMINI_API_KEY=<your key>
SUPABASE_URL=<your supabase project>
SUPABASE_SERVICE_KEY=<your service key>
BEACON_WALLET_BASE=<your base wallet>
BEACON_WALLET_SOLANA=<your solana wallet>

Kubernetes Deployment

For production-grade deployments:
k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: beacon
  labels:
    app: beacon
spec:
  replicas: 3
  selector:
    matchLabels:
      app: beacon
  template:
    metadata:
      labels:
        app: beacon
    spec:
      containers:
      - name: beacon
        image: your-registry/beacon:latest
        ports:
        - containerPort: 8080
        env:
        - name: REDIS_URL
          valueFrom:
            secretKeyRef:
              name: beacon-secrets
              key: redis-url
        - name: GEMINI_API_KEY
          valueFrom:
            secretKeyRef:
              name: beacon-secrets
              key: gemini-api-key
        - name: RUST_LOG
          value: "info"
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: beacon
spec:
  selector:
    app: beacon
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: LoadBalancer
Deploy:
# Create secrets
kubectl create secret generic beacon-secrets \
  --from-literal=redis-url=redis://redis-service:6379 \
  --from-literal=gemini-api-key=your_key

# Deploy application
kubectl apply -f k8s/deployment.yaml

# Check status
kubectl get pods -l app=beacon
kubectl logs -l app=beacon -f

Scaling Strategies

Horizontal Scaling

Beacon is stateless and can be horizontally scaled: Docker Compose:
docker-compose up -d --scale beacon=5
Kubernetes HPA:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: beacon-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: beacon
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

Load Balancing

Nginx Reverse Proxy:
upstream beacon {
    least_conn;
    server beacon-1:8080;
    server beacon-2:8080;
    server beacon-3:8080;
}

server {
    listen 80;
    server_name beacon-api.example.com;

    location / {
        proxy_pass http://beacon;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Redis Scaling

For high traffic, use Redis Cluster or managed services: Redis Sentinel (HA):
redis-master:
  image: redis:7-alpine
  command: redis-server --appendonly yes

redis-sentinel:
  image: redis:7-alpine
  command: redis-sentinel /etc/redis/sentinel.conf
Managed Redis:
  • AWS ElastiCache: rediss://master.xxx.cache.amazonaws.com:6379
  • Google Cloud Memorystore: Built-in HA
  • Azure Cache for Redis: Premium tier with clustering

Monitoring

Health Checks

Beacon provides a health endpoint:
curl http://localhost:8080/health
Response:
{
  "status": "ok",
  "version": "0.2.4",
  "name": "beacon"
}

Logging

Beacon uses tracing for structured logging:
# Set log level
export RUST_LOG=debug

# Module-specific logging
export RUST_LOG=beacon=debug,axum=info,redis=warn
Example Logs:
2024-03-04T12:00:00.123Z INFO beacon: Starting Beacon API server port=8080
2024-03-04T12:00:01.456Z DEBUG beacon: Processing generate request repo="my-project"
2024-03-04T12:00:02.789Z ERROR beacon: Redis connection error error="Connection refused"

Metrics Collection

Add Prometheus metrics (requires prometheus crate):
use prometheus::{Counter, Histogram, Registry};

lazy_static! {
    static ref REQUEST_COUNTER: Counter = Counter::new(
        "beacon_requests_total",
        "Total number of requests"
    ).unwrap();
    
    static ref REQUEST_DURATION: Histogram = Histogram::new(
        "beacon_request_duration_seconds",
        "Request duration in seconds"
    ).unwrap();
}

// In middleware
REQUEST_COUNTER.inc();
let timer = REQUEST_DURATION.start_timer();
// ... process request ...
timer.observe_duration();
Expose metrics endpoint:
.route("/metrics", get(metrics_handler))

Distributed Tracing

Integrate with OpenTelemetry:
Cargo.toml
[dependencies]
opentelemetry = "0.20"
opentelemetry-jaeger = "0.19"
tracing-opentelemetry = "0.21"
use opentelemetry::global;
use tracing_subscriber::layer::SubscriberExt;

let tracer = opentelemetry_jaeger::new_pipeline()
    .with_service_name("beacon")
    .install_simple()?;

let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
tracing_subscriber::registry()
    .with(telemetry)
    .init();

Security Hardening

TLS/HTTPS

Option 1: Nginx Termination
server {
    listen 443 ssl http2;
    server_name beacon-api.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        proxy_pass http://beacon:8080;
    }
}
Option 2: Native Axum TLS (requires axum-server with TLS feature):
use axum_server::tls_rustls::RustlsConfig;

let config = RustlsConfig::from_pem_file(
    "/path/to/cert.pem",
    "/path/to/key.pem"
).await?;

axum_server::bind_rustls(addr, config)
    .serve(app.into_make_service())
    .await?;

API Key Authentication

Add authentication middleware:
async fn auth_middleware(
    headers: HeaderMap,
    request: Request<Body>,
    next: Next,
) -> Result<Response, StatusCode> {
    let api_key = headers
        .get("x-api-key")
        .and_then(|h| h.to_str().ok())
        .ok_or(StatusCode::UNAUTHORIZED)?;
    
    if !validate_api_key(api_key).await {
        return Err(StatusCode::UNAUTHORIZED);
    }
    
    Ok(next.run(request).await)
}

CORS Configuration

use tower_http::cors::{CorsLayer, Any};

let cors = CorsLayer::new()
    .allow_origin("https://your-frontend.com".parse::<HeaderValue>().unwrap())
    .allow_methods([Method::GET, Method::POST])
    .allow_headers(Any);

let app = Router::new()
    // ... routes ...
    .layer(cors);

Rate Limit Bypassing Protection

Prevent header spoofing:
// Only trust X-Forwarded-For from known proxies
const TRUSTED_PROXIES: &[&str] = &["10.0.0.0/8", "172.16.0.0/12"];

let ip = if is_trusted_proxy(&connection_ip) {
    extract_forwarded_ip(&headers)
} else {
    connection_ip
};

Secret Management

Option 1: Environment Variables
# Use secret management services
export GEMINI_API_KEY=$(aws secretsmanager get-secret-value --secret-id beacon/gemini-key --query SecretString --output text)
Option 2: Kubernetes Secrets
apiVersion: v1
kind: Secret
metadata:
  name: beacon-secrets
type: Opaque
data:
  gemini-api-key: <base64-encoded-key>
Option 3: HashiCorp Vault
let client = vaultrs::client::VaultClient::new(...);
let secret: String = client.kv2::read("secret/data/beacon", "gemini-api-key").await?;

Database Setup

Supabase Tables

Create the required tables in Supabase:
-- Runs table
CREATE TABLE runs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  repo_name TEXT NOT NULL,
  provider TEXT NOT NULL,
  status TEXT NOT NULL CHECK (status IN ('pending', 'paid', 'complete', 'failed')),
  txn_hash TEXT,
  chain TEXT,
  agents_md TEXT,
  error TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Payments table
CREATE TABLE payments (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  run_id UUID REFERENCES runs(id) ON DELETE CASCADE,
  txn_hash TEXT UNIQUE NOT NULL,
  chain TEXT NOT NULL,
  amount_usdc NUMERIC(10, 6) NOT NULL,
  from_address TEXT,
  confirmed BOOLEAN DEFAULT true,
  confirmed_at TIMESTAMPTZ DEFAULT NOW(),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Indexes for performance
CREATE INDEX idx_runs_status ON runs(status);
CREATE INDEX idx_payments_txn_hash ON payments(txn_hash);
CREATE INDEX idx_payments_run_id ON runs(run_id);

Connection Pooling

For high traffic, configure Supabase connection pooling:
# Use connection pooler endpoint
SUPABASE_URL=https://your-project.supabase.co
# Add ?pgbouncer=true for pooling
POSTGRES_URL=postgresql://postgres:password@db.your-project.supabase.co:6543/postgres?pgbouncer=true

Custom AI Provider Endpoints

Self-Hosted Gemini/Claude

Override API endpoints:
src/inferrer.rs:18-23
const GEMINI_URL: &str = std::env::var("GEMINI_ENDPOINT")
    .unwrap_or("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent");

Local LLM Integration

Add support for local models (Ollama, LM Studio):
async fn call_local_llm(prompt: &str) -> Result<AgentsManifest> {
    let endpoint = std::env::var("LOCAL_LLM_ENDPOINT")
        .unwrap_or("http://localhost:11434/api/generate".to_string());
    
    let response = CLIENT
        .post(endpoint)
        .json(&json!({
            "model": "llama3",
            "prompt": prompt,
            "stream": false
        }))
        .send()
        .await?;
    
    // Parse response...
}

Performance Optimization

Caching Layer

Add Redis caching for repeated requests:
async fn handle_generate(req: GenerateRequest) -> Result<GenerateResponse> {
    let cache_key = format!("generate:{}", hash_request(&req));
    
    // Check cache
    if let Ok(cached) = redis.get::<_, String>(&cache_key).await {
        return Ok(serde_json::from_str(&cached)?);
    }
    
    // Generate and cache
    let result = generate_agents_md(req).await?;
    redis.setex(&cache_key, 3600, serde_json::to_string(&result)?).await?;
    
    Ok(result)
}

Connection Keep-Alive

Reuse HTTP connections:
src/inferrer.rs:8-13
static CLIENT: Lazy<Client> = Lazy::new(|| {
    Client::builder()
        .use_rustls_tls()
        .pool_max_idle_per_host(10)
        .build()
        .expect("Failed to create reqwest client")
});

Binary Size Reduction

Optimize for smaller Docker images:
Cargo.toml
[profile.release]
opt-level = "z"     # Optimize for size
lto = true          # Link-time optimization
codegen-units = 1   # Better optimization
strip = true        # Strip debug symbols

Disaster Recovery

Backup Strategy

  1. Redis Backups: Enable AOF and RDB snapshots
  2. Database Backups: Supabase automatic daily backups
  3. Configuration Backups: Version control all configs

High Availability

# Multi-region deployment
regions:
  - us-east-1 (primary)
  - eu-west-1 (failover)
  - ap-southeast-1 (failover)

# Health check-based routing
route53:
  health_check: /health
  failover_threshold: 3

Cost Optimization

Free Tier Limits

  • Render: 750 hours/month free
  • Supabase: 500MB database, 2GB bandwidth
  • Redis Cloud: 30MB free
  • Gemini: Free tier available

Resource Limits

docker-compose.yml
beacon:
  deploy:
    resources:
      limits:
        cpus: '0.5'
        memory: 256M
      reservations:
        cpus: '0.25'
        memory: 128M

Checklist

Before going to production:
  • Enable HTTPS/TLS
  • Configure proper CORS
  • Set up monitoring and alerts
  • Enable Redis persistence
  • Configure database backups
  • Set resource limits
  • Implement health checks
  • Add structured logging
  • Rotate API keys
  • Test failover scenarios
  • Document runbooks
  • Set up CI/CD pipeline

Next Steps

Configuration

Review all environment variables

Rate Limiting

Customize rate limits for production

Build docs developers (and LLMs) love