ActiveStorage::CloudTransformations
A Rails gem that extends ActiveStorage to generate image variants and video previews via external cloud services (like AWS Lambda) instead of processing them locally on your server.
Features
- Offload image and video processing to cloud services, reducing server load
- Support for variants - Transform images with custom dimensions, formats, and rotations
- Support for previews - Generate preview images from video files
- Flexible configuration - Easily point to your own cloud transformation service
- Per-instance endpoints - Route different requests to different endpoints based on your model logic
- Presigned URLs - Remove the need for your cloud service to have direct S3 access
- Non-blocking - Processing happens asynchronously without blocking request handling
- Rails 7.2+ - Works with modern Rails and ActiveStorage
Why Cloud Transformations?
Processing large images and videos locally consumes significant CPU resources. This gem lets you delegate that work to dedicated cloud services, allowing your Rails application to focus on what it does best. You get:
- Reduced server CPU usage
- Faster request handling
- Scalable processing as your media grows
Configuration
Global Configuration
Create an initializer at config/initializers/active_storage_cloud_transformations.rb:
ActiveStorage::CloudTransformations.configure do |config|
# The cloud service endpoint (default: "https://huuabwxpqf.execute-api.us-west-2.amazonaws.com/prod")
config.crucible_endpoint = ENV.fetch("CRUCIBLE_ENDPOINT", "https://crucible.example.com/prod")
# Use presigned URLs instead of S3 paths (default: false)
# When enabled, your cloud service doesn't need direct S3 access
config.use_presigned_urls = true
# Presigned URL expiration in seconds (default: 3600)
config.presigned_url_expiration = 7200
endPer-Instance Endpoint Configuration
You can configure different endpoints for different model instances by defining a crucible_endpoint method on your model:
class User < ApplicationRecord
has_one_attached :avatar
def crucible_endpoint
# Route premium users to a different endpoint
if premium?
"https://premium.crucible.example.com/prod"
elsif region == "eu"
"https://eu.crucible.example.com/prod"
else
# Return nil to use the global config
nil
end
end
endWhen processing attachments, the gem will:
- Check if the model defines a
crucible_endpointmethod - Use that endpoint if it returns a non-nil value
- Fall back to the global
config.crucible_endpointotherwise
Usage
Image Variants
Generate image variants just like you normally would with ActiveStorage. The cloud service will handle the transformation:
@user = User.find(1)
# Generate a variant (will be processed by cloud service)
@user.avatar.variant(resize_to_limit: [100, 100]).processedVideo Previews
Generate preview images from video files:
@video = Video.find(1)
# Generate a preview image from the video
@video.file.preview(resize_to_limit: [160, 160]).processed
# Access the generated preview image
image_url = @video.file.preview_image.urlSupported Transformations
The cloud service receives the following information via HTTP POST:
For image variants:
-
blob_url- URL to the source image (presigned GET URL or S3 path) -
dimensions- Target dimensions (e.g., "100x100") -
rotation- Rotation angle in degrees -
variant_url- Where to upload the processed variant (presigned PUT URL or S3 path) -
format- Output format (e.g., "webp", "jpeg")
For video previews:
-
blob_url- URL to the source video (presigned GET URL or S3 path) -
dimensions- Dimensions for the preview image -
rotation- Rotation angle in degrees -
preview_image_url- Where to upload the generated preview image (presigned PUT URL or S3 path) -
preview_image_variant_url- Where to upload the preview image variant (presigned PUT URL or S3 path)
Note: When use_presigned_urls is enabled, all URLs include AWS signature query parameters. When disabled, URLs are S3 paths without query parameters, requiring your cloud service to have S3 credentials.
How It Works
- When you request a variant or preview, ActiveStorage creates blob records
- This gem intercepts the process and makes an HTTP POST request to your cloud service
- The request includes:
- Source media URL (presigned GET URL or S3 path)
- Destination URL for the processed file (presigned PUT URL or S3 path)
- Transformation parameters (dimensions, format, rotation, etc.)
- Your cloud service performs the transformation:
- With presigned URLs: Downloads from the GET URL, transforms, uploads to the PUT URL
- Without presigned URLs: Accesses S3 directly using its own credentials
- The gem receives a 201 confirmation and marks the variant as processed
- Future requests for the same variant are served from the cache (already in S3)
Presigned URLs vs S3 Paths
Presigned URLs (recommended):
- Your cloud service doesn't need S3 credentials
- More secure - temporary, scoped access
- Works with any HTTP client
- Enable with
config.use_presigned_urls = true
S3 Paths (legacy):
- Your cloud service needs AWS credentials with S3 access
- URLs are simple paths like
s3://bucket/key - Default behavior for backward compatibility
Storage Requirements
This gem requires S3 as the storage service for your Rails application. It does not work with the local disk service.
For your Rails application:
- Configure ActiveStorage to use S3
- Needs S3 credentials to generate presigned URLs (when enabled) or direct S3 access
For your cloud transformation service:
- With presigned URLs enabled: No S3 credentials needed - the service uses presigned URLs
- Without presigned URLs: Requires AWS credentials with S3 read/write access
Development
Running Tests Locally
-
Copy the example environment file:
cp .env.example .env
-
Edit
.envwith your S3 credentials:AWS_S3_URI=s3://YOUR_ACCESS_KEY_ID:YOUR_SECRET_ACCESS_KEY@YOUR_REGION.amazonaws.com/YOUR_BUCKET
-
Run the tests:
bundle exec rspec
By default, requests are mocked in tests. Set CRUCIBLE_ENDPOINT in your .env file to point to a real service for full integration testing.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/botandrose/active_storage-cloud_transformations.
License
The gem is available as open source under the terms of the MIT License.