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
| Component | Requirement | Notes |
|---|---|---|
| CPU | 2 cores x86_64 / aarch64 | No special instructions required |
| RAM | 256 MB (deterministic-only) | Guardrail pipeline without LLM tier |
| RAM | 2 GB (with LLM tier) | Additional RAM for the LLM inference process |
| Disk | 50 MB (binary + policy data) | Policy TOML plus compiled binary |
| Disk | 2–8 GB (with LLM tier) | Local LLM model weights (e.g., llama3.1:8b via Ollama) |
Recommended Hardware (Production)
| Component | Recommendation |
|---|---|
| CPU | 4+ cores |
| RAM | 4 GB (deterministic-only), 16 GB (with LLM tier) |
| Network | Low-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
| Binary | Location | Purpose |
|---|---|---|
guardrail_serve | target/release/guardrail_serve | HTTP API server |
guardrail_measure | target/release/guardrail_measure | Benchmark measurement |
guardrail_realworld | target/release/guardrail_realworld | Real-world corpus evaluation |
guardrail_fuzz_mapper | target/release/guardrail_fuzz_mapper | Command classification fuzzer |
guardrail_mutation_fuzz | target/release/guardrail_mutation_fuzz | Mutation-based fuzzing |
guardrail_coverage_fuzz | target/release/guardrail_coverage_fuzz | Coverage-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
| Variable | Default | Description |
|---|---|---|
GUARDRAIL_HOST | 127.0.0.1 | Bind address for the HTTP server |
GUARDRAIL_PORT | 9090 | Bind port for the HTTP server |
GUARDRAIL_LLM_URL | http://127.0.0.1:11434 | Ollama-compatible LLM endpoint URL. Only used when LLM tier is enabled. |
GUARDRAIL_LLM_MODEL | llama3.1:8b | Model name for the LLM judge. Used with the URL above. |
CLI Flags
| Flag | Default | Description |
|---|---|---|
--host <HOST> | 127.0.0.1 | Override GUARDRAIL_HOST env var |
--port <PORT> | 9090 | Override 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 /healthendpoint provides a light-weight liveness signal - The
GET /metricsendpoint provides per-instance counters
Deployment Topology Options
| Topology | Session Detection | Complexity | Use Case |
|---|---|---|---|
| Single instance | Full | Low | Development, low-traffic |
| Sticky-session LB | Full | Medium | Production with session tracking |
| Round-robin LB | Per-turn only | Low | Production without session tracking |
| Sidecar per process | Full | Medium | Kubernetes 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.