A lightweight, high-performance Nx cache server that bridges Nx CLI clients with cloud storage providers for caching build artifacts. Built in Rust with a focus on maximum performance and minimal memory usage - less than 4MB during regular operation! 🚀
- AWS S3 Integration: Direct streaming integration with AWS S3 and S3-compatible services
- Memory Efficient: Direct streaming with less than 4MB RAM usage during typical operation
- High Performance: Built with Rust and Axum for maximum throughput
- Zero Dependencies: Self-contained single executable with no external dependencies required
- Nx API Compliant: Full implementation of the Nx custom remote cache OpenAPI specification
- Security First: Bearer token authentication with constant-time comparison
- Self-Hosted & Private: Full control over your data with zero telemetry
Access to AWS S3 (or S3-compatible service like MinIO)
Go to Releases page and download the binary for your operating system.
Alternatively, use command line tools:
# Using curl
curl -L https://github.com/nxcite/nx-cache-server/releases/download/<VERSION>/nx-cache-aws-<VERSION>-<PLATFORM> -o nx-cache-aws
# Using wget
wget https://github.com/nxcite/nx-cache-server/releases/download/<VERSION>/nx-cache-aws-<VERSION>-<PLATFORM> -O nx-cache-aws
# Replace:
# <VERSION> with the version tag (e.g., v1.2.0)
# <PLATFORM> with your platform (e.g., linux-x86_64, macos-arm64, macos-x86_64, windows-x86_64.exe).chmod +x nx-cache-awsThe server supports configuration via environment variables, command-line arguments, or both.
# Required
export S3_BUCKET_NAME="your-s3-bucket-name"
export SERVICE_ACCESS_TOKEN="your-bearer-token"
# AWS Credentials (optional - auto-discovered from IAM roles, config files, SSO if not provided)
export AWS_ACCESS_KEY_ID="your-aws-access-key-id"
export AWS_SECRET_ACCESS_KEY="your-aws-secret-access-key"
export AWS_SESSION_TOKEN="your-session-token" # If you are using temporary credentials
# AWS Region (optional - auto-discovered from AWS config, EC2/ECS metadata if not provided)
export AWS_REGION="us-west-2"
# Optional
export S3_ENDPOINT_URL="your-s3-endpoint-url" # For S3-compatible services like MinIO
export S3_TIMEOUT="30" # S3 operation timeout in seconds (default: 30)
export PORT="3000" # Server port (default: 3000)
export BIND_ADDRESS="0.0.0.0" # IP to bind to (default: 0.0.0.0). Use "::" for IPv6/dual-stack./nx-cache-aws \
--region "your-aws-region" \
--access-key-id "your-aws-access-key-id" \
--secret-access-key "your-aws-secret-access-key" \
--bucket-name "your-s3-bucket-name" \
--session-token "your-session-token" \
--endpoint-url "your-s3-endpoint-url" \
--service-access-token "your-bearer-token" \
--timeout-seconds 30 \
--port 3000 \
--bind-address 0.0.0.0You can also combine both methods. Command line arguments will override environment variables:
# Set common config via environment
export AWS_REGION="us-west-2"
export S3_BUCKET_NAME="my-cache-bucket"
export SERVICE_ACCESS_TOKEN="my-secure-token"
# Specify other values via CLI
./nx-cache-aws --port 8080Note: AWS credentials and region are optional when running on AWS infrastructure (EC2, ECS, Lambda) or when AWS config files are present. The server will auto-discover them from your environment.
./nx-cache-awscurl http://localhost:3000/healthYou should receive an "OK" response.
To configure your Nx workspace to use this cache server, set the following environment variables:
# Point Nx to your cache server
export NX_SELF_HOSTED_REMOTE_CACHE_SERVER="http://localhost:3000"
# Authentication token (must match SERVICE_ACCESS_TOKEN from server config)
export NX_SELF_HOSTED_REMOTE_CACHE_ACCESS_TOKEN="your-bearer-token"
# Optional: Disable TLS certificate validation (e.g. for development/testing environment)
export NODE_TLS_REJECT_UNAUTHORIZED="0"Once configured, Nx will automatically use your cache server for storing and retrieving build artifacts.
For more details, see the Nx documentation.
This fork's operating rule: RUST_LOG=nx_cache_server=info must be sufficient to fully diagnose any 4xx/5xx response, without ever needing debug logging. This matters because enabling debug logging on the whole AWS SDK (RUST_LOG=debug) generates enough volume to rotate out of a small log window in a couple of minutes on a busy cache server - exactly what happened during an incident that motivated this section.
At INFO level you get:
- One access-log line per request (method, path, status,
duration_ms, andbyteswhen aContent-Lengthis set) - so you can always tell whether/when/how a request finished. - One structured error line per failure, carrying
operation(head/get/put/multipart-create/multipart-part/multipart-complete/multipart-abort), the cache object hash, and the AWS SDK's own request id(s) plus the full error detail - enough to open an AWS support case or match logs to an S3-side incident without re-running anything at debug. - Client-side aborts are logged at
WARN, notERROR, and answered with400 Bad Requestinstead of500. A client disconnecting or resetting mid-upload is not a server failure and must not page anyone or count toward server error budgets.
Run with, e.g.:
export RUST_LOG="nx_cache_server=info"PUT bodies are streamed to S3 in ~8MiB chunks. Small bodies use a single PutObject; anything larger uses S3 multipart upload (CreateMultipartUpload / UploadPart / CompleteMultipartUpload), so the server never buffers more than roughly one part of a (potentially multi-GB) build artifact in memory at a time. If any part fails to upload (including the client disconnecting mid-stream), the in-progress multipart upload is aborted via AbortMultipartUpload.
Recommended: because a process crash, network partition, or bug could in principle still leave an incomplete multipart upload behind (incomplete parts are billed S3 storage with no object to show for it), add an S3 Lifecycle rule to the cache bucket that expires incomplete multipart uploads after a few days, e.g.:
{
"Rules": [
{
"ID": "abort-incomplete-multipart-uploads",
"Status": "Enabled",
"AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 3 }
}
]
}This makes cleanup self-healing even in failure modes the server-side abort can't reach (e.g. the process being killed before it runs).