Deployment Guide

Guardrail is distributed under a commercial license agreement. To get the source, request access — it ships to licensees. You can also see it decide on real commands in the demo walkthrough before installing. See the quickstart for the fastest path to a running instance.

System Requirements

Minimum Hardware

ComponentRequirementNotes
CPU2 cores x86_64 / aarch64No special instructions required
RAM256 MB (deterministic-only)Guardrail pipeline without LLM tier
RAM2 GB (with LLM tier)Additional RAM for the LLM inference process
Disk50 MB (binary + policy data)Policy TOML plus compiled binary
Disk2–8 GB (with LLM tier)Local LLM model weights (e.g., llama3.1:8b via Ollama)
ComponentRecommendation
CPU4+ cores
RAM4 GB (deterministic-only), 16 GB (with LLM tier)
NetworkLow-latency (<5ms) connection to LLM endpoint if used

Software Dependencies

  • Rust toolchain: edition 2021, MSRV 1.75+ (stable)
  • System packages (build-time only): build-essential, pkg-config, libssl-dev
  • Docker (optional, for containerized deployment): Docker Engine 24+
  • systemd (optional, for Linux service management): systemd 247+
  • nginx (optional, for reverse proxy): nginx 1.24+

Building From Source

Prerequisites

# Install Rust toolchain (if not present)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Verify
rustc --version  # Must be 1.75+
cargo --version

Build

Once you have the source (shipped after requesting access):

# Build the guardrail server binary (release mode)
cargo build --release -p ellm-guardrail --bin guardrail_serve

# Build all guardrail binaries
cargo build --release -p ellm-guardrail

# Run tests
cargo test -p ellm-guardrail

The compiled binary will be at target/release/guardrail_serve.

Build Artifacts

BinaryLocationPurpose
guardrail_servetarget/release/guardrail_serveHTTP API server
guardrail_measuretarget/release/guardrail_measureBenchmark measurement
guardrail_realworldtarget/release/guardrail_realworldReal-world corpus evaluation
guardrail_fuzz_mappertarget/release/guardrail_fuzz_mapperCommand classification fuzzer
guardrail_mutation_fuzztarget/release/guardrail_mutation_fuzzMutation-based fuzzing
guardrail_coverage_fuzztarget/release/guardrail_coverage_fuzzCoverage-guided fuzzing

Docker Deployment

Sample Dockerfile

Place this Dockerfile at the repository root:

FROM rust:1.75-slim-bookworm AS builder

WORKDIR /build
RUN apt-get update && apt-get install -y pkg-config libssl-dev && rm -rf /var/lib/apt/lists/*

# Copy the shipped source and build the server binary
COPY . .
RUN cargo build --release --bin guardrail_serve

# ── Runtime image ──
FROM debian:bookworm-slim

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

# Create non-root user
RUN groupadd -r guardrail && useradd -r -g guardrail -d /var/lib/guardrail -s /sbin/nologin guardrail

COPY --from=builder /build/target/release/guardrail_serve /usr/local/bin/guardrail_serve
# Bundled policy is embedded in the binary at compile time — no policy file to copy.
# Mount a custom policy at /etc/ellm-guardrail/policy.toml to override it.

WORKDIR /var/lib/guardrail
USER guardrail

EXPOSE 9090

HEALTHCHECK --interval=15s --timeout=5s --retries=3 \
    CMD ["/bin/sh", "-c", "curl -sf http://localhost:9090/health || exit 1"]

ENTRYPOINT ["guardrail_serve"]
CMD ["--host", "0.0.0.0", "--port", "9090"]

Building and Running the Docker Image

docker build -t ellm-guardrail:latest .
docker run -d \
  --name guardrail \
  -p 9090:9090 \
  -e GUARDRAIL_LLM_URL=http://ollama:11434 \
  -e GUARDRAIL_LLM_MODEL=llama3.1:8b \
  ellm-guardrail:latest

Systemd Service Unit

Create /etc/systemd/system/ellm-guardrail.service:

[Unit]
Description=Guardrail Policy Checker
Documentation=https://elai-intelligence.com/docs/deployment/
After=network.target
Wants=network-online.target

[Service]
Type=simple
User=guardrail
Group=guardrail
WorkingDirectory=/var/lib/guardrail

# Binary and arguments
ExecStart=/usr/local/bin/guardrail_serve --host 127.0.0.1 --port 9090

# Environment variables
Environment=GUARDRAIL_HOST=127.0.0.1
Environment=GUARDRAIL_PORT=9090
# Uncomment to enable LLM tier:
# Environment=GUARDRAIL_LLM_URL=http://localhost:11434
# Environment=GUARDRAIL_LLM_MODEL=llama3.1:8b

# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
CapabilityBoundingSet=
AmbientCapabilities=
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
MemoryDenyWriteExecute=true
LockPersonality=true

# Resource limits
LimitNOFILE=1024
LimitNPROC=64

# Restart policy
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable ellm-guardrail
sudo systemctl start ellm-guardrail

# Check status
sudo systemctl status ellm-guardrail

# View logs
sudo journalctl -u ellm-guardrail -f

Configuration Reference

Environment Variables

VariableDefaultDescription
GUARDRAIL_HOST127.0.0.1Bind address for the HTTP server
GUARDRAIL_PORT9090Bind port for the HTTP server
GUARDRAIL_LLM_URLhttp://127.0.0.1:11434Ollama-compatible LLM endpoint URL. Only used when LLM tier is enabled.
GUARDRAIL_LLM_MODELllama3.1:8bModel name for the LLM judge. Used with the URL above.

CLI Flags

FlagDefaultDescription
--host <HOST>127.0.0.1Override GUARDRAIL_HOST env var
--port <PORT>9090Override GUARDRAIL_PORT env var

Policy Data

The classification policy is bundled with the crate and embedded in the binary at compile time (include_str!) — a deployed binary needs no policy file to run. To customize it, layer a policy file at one of the resolved paths: /etc/ellm-guardrail/policy.toml, ~/.config/ellm-guardrail/policy.toml, ./guardrail.toml, or $GUARDRAIL_POLICY (highest precedence).

Important: Policy is loaded once at startup. There is no runtime policy reload — changing policy requires a process restart. Future versions may support reload without restart.

TLS Certificate Setup

The guardrail server itself is a minimal HTTP-only service. For production deployments, terminate TLS at the reverse proxy (see the nginx example below). If you need TLS directly on the server, wrap it behind a TLS-terminating proxy such as:

  • nginx (recommended)
  • HAProxy
  • Caddy
  • AWS ALB / GCP HTTPS Load Balancer
  • stunnel

Self-Signed Certificate for Development

# Generate a self-signed certificate
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout /etc/ssl/private/guardrail.key \
  -out /etc/ssl/certs/guardrail.crt \
  -subj "/CN=guardrail.local" \
  -addext "subjectAltName=DNS:guardrail.local,IP:127.0.0.1"

Reverse Proxy Setup (nginx)

Basic nginx Configuration

server {
    listen 443 ssl http2;
    server_name guardrail.example.com;

    ssl_certificate /etc/ssl/certs/guardrail.crt;
    ssl_certificate_key /etc/ssl/private/guardrail.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        proxy_pass http://127.0.0.1:9090;
        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;

        # Request size limits (tool call with params)
        client_max_body_size 1M;

        # Timeouts
        proxy_connect_timeout 5s;
        proxy_read_timeout 30s;
        proxy_send_timeout 10s;
    }

    # Health check endpoint — no auth required
    location = /health {
        proxy_pass http://127.0.0.1:9090;
        proxy_set_header Host $host;
        access_log off;
    }
}

# HTTP → HTTPS redirect
server {
    listen 80;
    server_name guardrail.example.com;
    return 301 https://$host$request_uri;
}

With API Key Authentication (nginx plus)

server {
    listen 443 ssl http2;
    server_name guardrail.example.com;

    # ... SSL config as above ...

    location / {
        # API key validation via Lua or auth_request
        auth_request /_auth;

        proxy_pass http://127.0.0.1:9090;
        # ... proxy settings as above ...
    }

    location = /_auth {
        internal;
        # Simple key check via map or external auth service
        proxy_method GET;
        proxy_pass http://127.0.0.1:9091;  # External auth service
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Original-URI $request_uri;
    }
}

Health Check Configuration

Endpoint

GET /health

Response

{"status":"ok"}

HTTP Status Codes

  • 200 OK: Service is healthy and accepting requests
  • 5xx (no response or connection refused): Service is down

Health Check Examples

Docker health check (built into the Dockerfile above):

HEALTHCHECK --interval=15s --timeout=5s --retries=3 \
    CMD ["/bin/sh", "-c", "curl -sf http://localhost:9090/health || exit 1"]

Kubernetes liveness probe:

livenessProbe:
  httpGet:
    path: /health
    port: 9090
  initialDelaySeconds: 5
  periodSeconds: 15
  timeoutSeconds: 5
  failureThreshold: 3

Kubernetes readiness probe:

readinessProbe:
  httpGet:
    path: /health
    port: 9090
  initialDelaySeconds: 3
  periodSeconds: 10

Kubernetes startup probe (for slow-starting deployments):

startupProbe:
  httpGet:
    path: /health
    port: 9090
  initialDelaySeconds: 1
  periodSeconds: 2
  failureThreshold: 15

AWS NLB Target Group:

Health check path: /health
Health check port: 9090 (or proxy port)
Healthy threshold: 2
Unhealthy threshold: 3
Timeout: 5 seconds
Interval: 15 seconds

Load Balancer Integration

The guardrail service is stateless at the individual check level. Each POST /check request is independent and can be routed to any node. Session-based chain detection (multi-turn recon detection) is in-memory per pipeline instance and does NOT span load-balanced replicas.

Implications

  • Session chain detection is local to each server instance
  • Sticky sessions (session affinity) are required if you use session-level chain detection
  • Without sticky sessions, recon chain detection degrades to single-turn analysis only
  • The GET /health endpoint provides a light-weight liveness signal
  • The GET /metrics endpoint provides per-instance counters

Deployment Topology Options

TopologySession DetectionComplexityUse Case
Single instanceFullLowDevelopment, low-traffic
Sticky-session LBFullMediumProduction with session tracking
Round-robin LBPer-turn onlyLowProduction without session tracking
Sidecar per processFullMediumKubernetes sidecar pattern

Metrics Endpoint

GET /metrics

Returns:

{
  "total_checks": 12345,
  "allow_count": 11000,
  "deny_count": 1000,
  "escalate_count": 345,
  "avg_latency_us": 19
}

This is a simple JSON counter snapshot. The server also exposes GET /metrics/prometheus, which serves the same counters in Prometheus text exposition format — point Prometheus straight at that endpoint. See the SLI/SLO guide for suggested queries and alert rules.