Deployment

Run Firn locally with Docker Compose or deploy to production as a standalone container.

Local development

The Docker Compose file launches MinIO (local S3) alongside the Firn API. No host Rust toolchain is required.

git clone https://github.com/gordonmurray/firnflow
cd firnflow
docker compose up --build

This starts three services:

ServicePortPurpose
minio9000 (S3 API), 9001 (console)S3-compatible object storage
minio-init-One-shot: creates the firnflow bucket
firnflow3000Firn API server

MinIO console: http://localhost:9001 (credentials: minioadmin / minioadmin)

Running just MinIO

If you want to run Firn outside Docker (e.g. during development with a host Rust toolchain), start only MinIO:

# Start MinIO only
docker compose up -d minio minio-init

# Run Firn directly
FIRNFLOW_S3_BUCKET=firnflow \
FIRNFLOW_S3_ENDPOINT=http://127.0.0.1:9000 \
FIRNFLOW_S3_ACCESS_KEY=minioadmin \
FIRNFLOW_S3_SECRET_KEY=minioadmin \
  cargo run -p firnflow-api

Production Docker

The included Dockerfile is a multi-stage build that produces a minimal production image.

Build the image

docker build -t firnflow-api .

Image details

StageBase imagePurpose
Builderrust:1.94-bookwormCompiles the release binary with protobuf support
Runtimedebian:bookworm-slimMinimal image with just ca-certificates for TLS

Run the container

docker run -d \
  --name firn \
  -p 3000:3000 \
  -e FIRNFLOW_S3_BUCKET=my-production-bucket \
  -e FIRNFLOW_S3_REGION=eu-west-1 \
  -e FIRNFLOW_CACHE_MEMORY_BYTES=268435456 \
  -e FIRNFLOW_CACHE_NVME_BYTES=10737418240 \
  -v firn-cache:/var/lib/firnflow/cache \
  firnflow-api
NVMe cache volume
Mount a fast local SSD at /var/lib/firnflow/cache for the NVMe tier. The cache is ephemeral: losing it only means cache misses until it warms up again. For best performance, use a tmpfs or NVMe-backed volume.

AWS deployment

For production on AWS, Firn works best with IAM-based credentials (instance profiles, ECS task roles, or IRSA for EKS). No access keys needed.

Prerequisites

  1. An S3 bucket with public access blocked
  2. An IAM role with s3:GetObject, s3:PutObject, s3:DeleteObject, s3:ListBucket on the bucket
  3. A compute environment (ECS, EKS, EC2) with the IAM role attached

Minimal IAM policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-firn-bucket",
        "arn:aws:s3:::my-firn-bucket/*"
      ]
    }
  ]
}

ECS task definition (excerpt)

{
  "containerDefinitions": [
    {
      "name": "firn",
      "image": "firnflow-api:latest",
      "portMappings": [{"containerPort": 3000}],
      "environment": [
        {"name": "FIRNFLOW_S3_BUCKET", "value": "my-firn-bucket"},
        {"name": "FIRNFLOW_S3_REGION", "value": "eu-west-1"},
        {"name": "FIRNFLOW_CACHE_MEMORY_BYTES", "value": "268435456"},
        {"name": "FIRNFLOW_CACHE_NVME_BYTES", "value": "10737418240"}
      ],
      "healthCheck": {
        "command": ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"],
        "interval": 10,
        "timeout": 3,
        "retries": 3
      },
      "mountPoints": [
        {
          "sourceVolume": "cache",
          "containerPath": "/var/lib/firnflow/cache"
        }
      ]
    }
  ]
}

Health checks

Use GET /health for both liveness and readiness probes. The endpoint returns 200 with body ok as soon as the server is listening.

Docker Compose

healthcheck:
  test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
  interval: 10s
  timeout: 3s
  retries: 3

Kubernetes

livenessProbe:
  httpGet:
    path: /health
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 10
readinessProbe:
  httpGet:
    path: /health
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 5

Cache warmup after deployment

After a fresh deployment or restart, the cache is empty. Use the /ns/{ns}/warmup endpoint to pre-populate it with your most common queries:

curl -X POST http://localhost:3000/ns/production/warmup \
  -H 'Content-Type: application/json' \
  -d '{
    "queries": [
      {"vector": [1.0, 0.0, ...], "k": 10},
      {"vector": [0.0, 1.0, ...], "k": 10}
    ]
  }'

This returns immediately (202) and runs the queries in the background, populating the cache as it goes. Monitor firnflow_cache_misses_total to track warmup progress.

Recommended operational setup