Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/iii-hq/iii/llms.txt

Use this file to discover all available pages before exploring further.

This guide covers production deployment of iii with security hardening, monitoring, and high availability.

Hardened Docker Setup

Run iii with security constraints:
docker run --read-only --tmpfs /tmp \
  --cap-drop=ALL --cap-add=NET_BIND_SERVICE \
  --security-opt=no-new-privileges:true \
  -v ./config.yaml:/app/config.yaml:ro \
  -p 3111:3111 -p 49134:49134 -p 3112:3112 -p 9464:9464 \
  iiidev/iii:latest
Security flags:
  • --read-only - Read-only root filesystem
  • --tmpfs /tmp - Writable temporary directory
  • --cap-drop=ALL - Drop all Linux capabilities
  • --cap-add=NET_BIND_SERVICE - Allow binding to ports < 1024
  • --security-opt=no-new-privileges:true - Prevent privilege escalation

Docker Compose Production

Use docker-compose.prod.yml for production with Caddy reverse proxy:
docker-compose.prod.yml
services:
  caddy:
    image: caddy:2-alpine
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      iii:
        condition: service_healthy
    restart: unless-stopped

  iii:
    image: iiidev/iii:latest
    volumes:
      - ./config.prod.yaml:/app/config.yaml:ro
    healthcheck:
      test: ["CMD-SHELL", "nc -z 127.0.0.1 3111 || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    restart: unless-stopped

volumes:
  caddy_data:
  caddy_config:
Start production stack:
docker compose -f docker-compose.prod.yml up -d

Reverse Proxy (Caddy)

Caddy provides automatic HTTPS with Let’s Encrypt:
Caddyfile
your-domain.com {
	handle /api/* {
		reverse_proxy iii:3111
	}

	handle /streams/* {
		reverse_proxy iii:3112
	}

	handle /ws {
		reverse_proxy iii:49134
	}

	handle {
		reverse_proxy iii:3111
	}
}
Features:
  • Automatic HTTPS with Let’s Encrypt
  • HTTP/2 and HTTP/3 support
  • Automatic certificate renewal
  • WebSocket proxying
Replace your-domain.com with your actual domain. Caddy will automatically obtain SSL certificates.

Port Configuration

iii exposes four ports:
PortServiceExpose Publicly?Notes
49134WebSocketYesWorker SDK connections
3111HTTP APIVia proxyREST API endpoints
3112Stream APIVia proxyReal-time streams
9464MetricsNoInternal monitoring only
Recommended network setup:
  • Expose port 49134 directly for SDK connections (or via WSS proxy)
  • Proxy ports 3111 and 3112 through Caddy/nginx with HTTPS
  • Keep port 9464 internal (metrics scraping only)

Redis Setup

iii requires Redis for Queue and Stream modules.

Redis Standalone

redis:
  image: redis:7-alpine
  ports:
    - "6379:6379"
  volumes:
    - redis_data:/data
  command: redis-server --appendonly yes
  healthcheck:
    test: ["CMD", "redis-cli", "ping"]
    interval: 5s
    timeout: 3s
    retries: 5
  restart: unless-stopped
Enable persistence:
redis-server --appendonly yes

Redis Cluster

For high availability, use Redis Cluster or Redis Sentinel:
redis_url: redis://redis-cluster:6379
Update your config:
config.prod.yaml
modules:
  - class: modules::stream::StreamModule
    config:
      adapter:
        class: modules::stream::adapters::RedisAdapter
        config:
          redis_url: ${REDIS_URL:redis://redis-cluster:6379}

Production Configuration

Example production config with security best practices:
config.prod.yaml
modules:
  - class: modules::api::RestApiModule
    config:
      host: 0.0.0.0  # Bind all interfaces (behind reverse proxy)
      port: 3111
      default_timeout: 30000
      concurrency_request_limit: 2048
      cors:
        allowed_origins:
          - https://app.example.com
          - https://admin.example.com
        allowed_methods:
          - GET
          - POST
          - PUT
          - DELETE

  - class: modules::stream::StreamModule
    config:
      port: 3112
      host: 0.0.0.0
      adapter:
        class: modules::stream::adapters::RedisAdapter
        config:
          redis_url: ${REDIS_URL:redis://redis:6379}

  - class: modules::queue::QueueModule
    config:
      adapter:
        class: modules::queue::RedisAdapter
        config:
          redis_url: ${REDIS_URL:redis://redis:6379}

  - class: modules::state::StateModule
    config:
      adapter:
        class: modules::state::adapters::KvStore
        config:
          store_method: file_based
          file_path: /data/state_store.db

  - class: modules::kv_server::KvServer
    config:
      store_method: file_based
      file_path: /data/kv_store
      save_interval_ms: 5000

  - class: modules::observability::OtelModule
    config:
      enabled: true
      service_name: iii-production
      service_version: ${SERVICE_VERSION:0.2.0}
      service_namespace: production
      exporter: otlp
      endpoint: ${OTEL_ENDPOINT:http://otel-collector:4317}
      sampling_ratio: 0.1  # 10% sampling
      metrics_enabled: true
      logs_enabled: true
      logs_exporter: otlp
      logs_console_output: false  # Disable console in production

  - class: modules::cron::CronModule
    config:
      adapter:
        class: modules::cron::KvCronAdapter

  - class: modules::pubsub::PubSubModule
    config:
      adapter:
        class: modules::pubsub::LocalAdapter
Key differences from development:
  • host: 0.0.0.0 - Bind all interfaces (behind reverse proxy)
  • Specific CORS origins (not *)
  • OTLP exporter for traces and logs
  • Reduced sampling ratio (10%)
  • File-based storage with persistent volumes
  • Console logging disabled for logs

Environment Variables

Set production environment variables:
.env
REDIS_URL=redis://redis-cluster:6379
OTEL_ENABLED=true
OTEL_EXPORTER_TYPE=otlp
OTEL_ENDPOINT=http://otel-collector:4317
SERVICE_VERSION=1.0.0
SERVICE_NAMESPACE=production
RUST_LOG=info
Load with Docker Compose:
services:
  iii:
    env_file: .env

Monitoring

Prometheus Metrics

iii exposes Prometheus metrics on port 9464:
prometheus.yml
scrape_configs:
  - job_name: 'iii'
    static_configs:
      - targets: ['iii:9464']
Available metrics:
  • iii_invocations_total - Total function invocations
  • iii_invocations_error - Failed invocations
  • iii_workers_active - Active worker connections
  • iii_http_requests_total - HTTP requests
  • Custom metrics via OpenTelemetry

Health Checks

iii provides health check endpoints: HTTP health check:
curl http://localhost:3111/health
Docker healthcheck:
healthcheck:
  test: ["CMD-SHELL", "nc -z 127.0.0.1 3111 || exit 1"]
  interval: 10s
  timeout: 5s
  retries: 5
  start_period: 10s

OpenTelemetry

Export traces and logs to OpenTelemetry Collector:
- class: modules::observability::OtelModule
  config:
    enabled: true
    exporter: otlp
    endpoint: http://otel-collector:4317
    logs_exporter: otlp
    metrics_exporter: otlp
Collector config:
otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317

exporters:
  prometheus:
    endpoint: "0.0.0.0:8889"
  logging:
    loglevel: debug

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [logging]
    metrics:
      receivers: [otlp]
      exporters: [prometheus]
    logs:
      receivers: [otlp]
      exporters: [logging]

Scaling

Horizontal Scaling

iii is stateless and can be scaled horizontally:
services:
  iii:
    image: iiidev/iii:latest
    deploy:
      replicas: 3
    volumes:
      - ./config.prod.yaml:/app/config.yaml:ro
Requirements:
  • Shared Redis instance for Queue and Stream modules
  • Load balancer for HTTP traffic (Caddy, nginx, HAProxy)
  • Session affinity not required

Load Balancing

Use Caddy with multiple upstream servers:
your-domain.com {
	reverse_proxy iii-1:3111 iii-2:3111 iii-3:3111 {
		lb_policy round_robin
		health_uri /health
	}
}

Backup and Recovery

Redis Backup

Enable Redis persistence:
redis-server --appendonly yes --save 60 1000
Backup Redis data:
docker exec redis redis-cli BGSAVE
docker cp redis:/data/dump.rdb ./backup/

State Storage Backup

Backup file-based state:
docker cp iii:/data/state_store.db ./backup/
docker cp iii:/data/kv_store ./backup/
Automated backups:
#!/bin/bash
BACKUP_DIR="/backups/$(date +%Y%m%d)"
mkdir -p $BACKUP_DIR

docker exec redis redis-cli BGSAVE
docker cp redis:/data/dump.rdb $BACKUP_DIR/
docker cp iii:/data/state_store.db $BACKUP_DIR/
docker cp iii:/data/kv_store $BACKUP_DIR/

Security Checklist

1

Run as non-root user

Use distroless image or create non-root user in Dockerfile
2

Read-only filesystem

Run with --read-only and mount writable volumes only where needed
3

Drop capabilities

Use --cap-drop=ALL --cap-add=NET_BIND_SERVICE
4

Network isolation

Use Docker networks, don’t expose internal ports
5

HTTPS/TLS everywhere

Use Caddy or nginx for automatic HTTPS
6

Restrict CORS origins

Set specific allowed origins in production config
7

Environment secrets

Use Docker secrets or external secret managers
8

Update regularly

Keep iii and dependencies updated

Troubleshooting

Connection Issues

Workers can’t connect to WebSocket (port 49134):
# Check if port is exposed
docker ps

# Test connection
telnet localhost 49134

# Check firewall
sudo ufw status

Redis Connection

iii can’t connect to Redis:
# Test Redis connection
redis-cli -h redis ping

# Check Redis logs
docker logs redis

# Verify REDIS_URL environment variable
docker exec iii env | grep REDIS

High Memory Usage

Reduce memory consumption:
# Lower sampling ratio
sampling_ratio: 0.05

# Reduce max spans
memory_max_spans: 5000

# Reduce metrics retention
metrics_retention_seconds: 1800

Logs

View iii logs:
# Docker logs
docker logs -f iii

# Compose logs
docker compose logs -f iii

# Set debug logging
docker run -e RUST_LOG=debug iiidev/iii:latest

Next Steps

Monitoring

Set up observability and alerting

Security

Advanced security hardening

Build docs developers (and LLMs) love