This Terraform configuration deploys an AWS Deadline Cloud service-managed fleet (SMF) whose workers read from a distant NFS filer through a KNFSD read cache, connected with a VPC resource endpoint.
The filer, an Amazon FSx for OpenZFS file system, is deployed in a second AWS region and reached over a VPC peering connection. That cross-region distance stands in for an on-premises or otherwise-distant filer, and is what makes a cache worth having: cache misses pay cross-region latency and inter-region data-transfer cost, while cache hits are served from the KNFSD proxy's RAM and local NVMe next to the workers.
Region A (compute) Region B (origin)
+-----------------------------+ +----------------------+
| Deadline Cloud SMF workers | resource endpoint | |
| (AWS-managed VPC) | (Deadline-managed | |
| | mount NFS | private DNS) | |
| v | | |
| VPC Lattice resource gw ---+-- VPC Lattice -------+ (your compute VPC) |
| | | | | |
| v | | | VPC peering |
| KNFSD proxy (RAM L1 + |---- NFSv3 over ------+--------+-----------> | FSx for OpenZFS
| local NVMe L2 cache) | peering link | | | (distant filer)
+-----------------------------+ +----------------------+
When is this pattern worth it? KNFSD helps when the origin is distant or bandwidth-limited (on-premises, cross-region, or a throughput-capped filer) and many workers repeatedly read the same data (render/simulation asset libraries). For a filer in the same region and AZ as the fleet, a cache adds a hop for little benefit, so mount the file system directly instead. This sample deploys cross-region so the cache has something to do. It is the reproducible stand-in for the on-premises case you cannot include in a repository.
- Terraform >= 1.5 and
git(Terraform clones the KNFSD module from GitHub duringterraform init). - AWS credentials for an account where Deadline Cloud is available, with permissions across both the compute and origin regions.
- Packer to build the KNFSD proxy AMI (see Setup step 1). No public or managed KNFSD AMI exists.
- The
deadlineCLI (pip install deadline) to submit the seed and benchmark jobs. - A Deadline Cloud monitor to view jobs (optional but recommended); see the starter farm README.
Service-managed fleet workers run in an AWS-managed VPC. The VPC resource endpoints feature lets them reach a resource in your VPC through VPC Lattice. This sample places a KNFSD caching proxy behind that endpoint, and points KNFSD at an FSx for OpenZFS filer in another region across a VPC peering connection.
| Resource | Region | Description |
|---|---|---|
aws_vpc / subnets / NAT / IGW (x2) |
A + B | Non-overlapping compute and origin VPCs |
aws_vpc_peering_connection (+ accepter, routes) |
A and B | Cross-region link the cache reads over |
aws_fsx_openzfs_file_system |
B | The distant origin filer (NFSv3) |
module.knfsd (ASG + NLB and more) |
A | KNFSD NFS caching proxy cluster |
aws_vpclattice_resource_gateway / aws_vpclattice_resource_configuration |
A | The resource endpoint into the compute VPC |
awscc_ram_resource_share |
A | Shares the resource configuration to fleets.deadline.amazonaws.com |
awscc_deadline_farm / awscc_deadline_queue / awscc_deadline_fleet / awscc_deadline_queue_fleet_association |
A | The Deadline Cloud farm, queue, and SMF |
aws_iam_role (x2) + policies |
A | Fleet and queue roles (see Troubleshooting for the required logs permission) |
Some behaviors are critical and easy to get wrong:
- The fleet role must grant
logs:CreateLogStream(plusPutLogEvents/GetLogEvents) scoped to/aws/deadline/<farmId>/*. Without it, SMF workers register but the agent cannot create its log stream, so workers get stuck inCREATED, never run jobs, and emit no logs. TheAWSDeadlineCloud-FleetWorkermanaged policy does not include CloudWatch Logs. This sample adds the inline policy, mirroring the starter-farm fleet role. - Workers reach the share via a Deadline-managed name, not the KNFSD or FSx DNS name:
<resource_config_id>.resource-endpoints.deadline.<region>.amazonaws.com. - KNFSD re-exports the origin's export path verbatim. FSx for OpenZFS exports
/fsx, so workers mount<endpoint>:/fsx. Mounting/returnsaccess denied by server. - Cross-region wiring: KNFSD points at the FSx file system's private IP (the primary
network interface's IP;
endpoint_ip_addressis only populated for Multi-AZ deployments) and reads over the peering link, because the FSx private DNS name does not resolve across regions. Traffic through the resource endpoint is one-way NAT, so the NFS mount usesnfsvers=3,proto=tcp,noresvport,nolock. - Deadline Cloud resources are only in the AWSCC provider; in-place fleet configuration
updates can fail validation, so change fleet configuration with
terraform apply -replace.
git clone https://github.com/awslabs/knfsd-file-cache.git
cd knfsd-file-cache/image
cat > image.pkrvars.hcl <<EOF
REGION = "us-west-2" # your compute_region
ARCH = ["amd64"] # match the fleet's x86_64 workers
EOF
packer init .
packer build -var-file=image.pkrvars.hcl .
# Note the resulting AMI ID for knfsd_proxy_ami.cp terraform.tfvars.example terraform.tfvars
# edit: compute_region, origin_region, knfsd_proxy_ami
terraform init # clones the pinned KNFSD module from GitHub
terraform applySeed the origin once, then run the fan-out read benchmark:
FARM=$(terraform output -raw farm_id)
QUEUE=$(terraform output -raw queue_id)
# Populate the distant filer with a synthetic asset library (run once).
deadline bundle submit ./job_bundles/seed --farm-id "$FARM" --queue-id "$QUEUE"
# Fan-out read benchmark: N concurrent tasks read the shared library.
deadline bundle submit ./job_bundles/benchmark --farm-id "$FARM" --queue-id "$QUEUE" \
-p FanoutTasks=16 -p ReadFilesPerTask=200The cache's value shows up in three ways; measure at least the first two:
- Origin offload (the clearest signal). In CloudWatch, watch the FSx origin's
DataReadBytesas you increaseFanoutTasks. With the cache, origin reads stay flat after the working set is warm, because KNFSD absorbs the fleet's read fan-out instead of paying cross-region every time. - Cold versus warm. The first benchmark run (or first task on a fresh KNFSD node) pays
cross-region misses; re-running reads the same files from cache. Compare
throughput_MiBpsandsecondsin the task logs between the cold and warm runs. - Fan-out scaling. Aggregate read throughput across tasks rises with
FanoutTaskswhen cached, but is capped by the origin's cross-region bandwidth when not.
To compare against no cache, deploy a second fleet whose resource endpoint targets the FSx
IP directly (skip the module.knfsd layer) and run the same benchmark; the direct-mount fleet
reads cross-region on every access.
A run of this sample (compute in us-west-2, origin FSx for OpenZFS in us-east-1, one
i3en.2xlarge KNFSD node, a 2-worker Spot fleet, 16 tasks x 150 files x 16 MiB = 38.4 GiB of
reads over the shared library):
- Per-task read throughput (cold to warm): the first task to touch the working set ran at about 19 MiB/s (paying cross-region latency on every miss); once the set was warm in KNFSD, tasks ran at about 230-600 MiB/s, roughly a 13x-30x speedup on the cached path.
- Origin offload: during the benchmark window the FSx origin in
us-east-1served about 0 MiB ofDataReadByteswhile the fleet read 38.4 GiB, because KNFSD absorbed the fan-out from its RAM and NVMe cache.
Your numbers will vary with region pair, instance type, working-set size, and fan-out width. The point is the shape: cold reads are slow and hit the distant origin; warm reads are fast and local, and the origin serves almost no reads.
Key input variables (see variables.tf for the full set and defaults):
| Variable | Default | Purpose |
|---|---|---|
compute_region |
us-west-2 |
Region A: KNFSD, resource endpoint, and the SMF |
origin_region |
us-east-1 |
Region B: the FSx for OpenZFS origin (must differ) |
knfsd_proxy_ami |
(required) | KNFSD AMI built in Setup step 1 |
knfsd_instance_type |
i3en.2xlarge |
KNFSD proxy instance (local NVMe for the L2 cache) |
fleet_market_type |
spot |
Fleet EC2 market type |
Seed and benchmark job parameters are documented in
job_bundles/seed/template.yaml and
job_bundles/benchmark/template.yaml.
Outputs (see outputs.tf) include farm_id, queue_id, fleet_id, the KNFSD
NLB address, and the worker-facing worker_resource_endpoint_dns_name.
This sample is advanced and is not free to run: two regions, two VPCs with NAT gateways,
a cross-region peering connection, an FSx for OpenZFS file system, a KNFSD EC2 instance
(i3en/i4i for local NVMe), a Network Load Balancer, VPC Lattice, and inter-region data
transfer for every cache miss.
The example NFS export options (no_root_squash, world-writable demo mount) favor simplicity;
tighten ownership and export ACLs on the origin for production use.
# Drain fleet workers first (set min/max to 0 via the console or update-fleet), then:
terraform destroyDeleting the VPC Lattice resource configuration can briefly fail while a Deadline-managed
endpoint association is still releasing; re-run terraform destroy once and it completes.
- Workers stay in
CREATEDand never run jobs, with no logs. The fleet role is missinglogs:CreateLogStream. This sample includes it; if you adapt the role, keep that permission. mount.nfs: access denied by server. You are mounting the wrong export path. KNFSD re-exports/fsx; mount<endpoint>:/fsx, not/.- KNFSD exports nothing (
showmount -e localhostis empty). The proxy discovered the origin at boot before the origin IP was available; terminate the KNFSD instance so the Auto Scaling group relaunches one with the correct configuration. - Fleet configuration change fails to apply in place. Use
terraform apply -replace='awscc_deadline_fleet.this'.
- Connect VPC resources to your SMF with VPC resource endpoints
- CloudFormation
smf_vpc_fsxreference - Terraform starter farm
- KNFSD (awslabs/knfsd-file-cache): Apache-2.0, referenced here and not vendored