How to Integrate Cloudflare R2 with Ruby on Rails 8 for Public and Private File Storage Using Your Own Custom Domain (Part 2)

Hi all, in this post we’ll walk you through integrating Cloudflare R2 with your Ruby on Rails 8 application for both public and private file storage using your own custom domain. Cloudflare R2 is a highly scalable object storage solution, and by using ActiveStorage, we can easily manage files in Rails.

If you haven’t set up Cloudflare R2 with Rails before, be sure to check out the installation guide on How to Integrate Cloudflare R2 with Ruby on Rails 8 for Public and Private File Storage for step-by-step instructions on getting started.

Update Active Storage Initializer

To ensure ActiveStorage generates the correct public URLs for your files, you’ll need to create a custom initializer that overrides the default URL generation logic.

Custom Initializer (config/initializers/active_storage_init.rb)

# frozen_string_literal: true

require "active_storage"
require "active_storage/service/s3_service"

module ActiveStorage
  class Service::S3Service
    def url(key, **options)
      if public? && upload_options[:public_url]
        "#{upload_options[:public_url]}/#{key}"
      else
        super
      end
    end
  end
end


CLOUDFLARE_R2_CONFIG = {
  public_url: "https://r2-custom-domain.roigrowthpro.com",
  access_key_id: Rails.application.credentials.dig(:r2, :access_key_id),
  secret_access_key: Rails.application.credentials.dig(:r2, :secret_access_key),
  private_bucket: "my-private-bucket",
  public_bucket:  "my-public-bucket",
  region: "weur", # R2 region
  endpoint: "https://xxxx.r2.cloudflarestorage.com", 
}

....

This monkey patched part checks if a file is public and, if so, it constructs the URL using the public URL defined in your configuration.

This is not harmful monkey patching. However, I recently encountered the following monkey patch in this repository: Sysrandom. Surprisingly, this repository is still being used in active projects like Fastlane and its dependencies, as evidenced here: Fastlane-sirp 🤦🏻‍♂️

The monkey patch overrides the ActiveSupport::SecureRandom constant, leading to runtime errors, such as “method not found” for newly added methods. It’s incredibly frustrating to see such issues, as developers should have the option to enable or disable such behavior explicitly. For instance, in this case, we explicitly enabled the public_url feature. Similarly, enabling such patches should follow an explicit, opt-in approach rather than being implicit, which would greatly enhance the development experience. I’m planning prepare blog post about this harmful monkey patching issues.

Configure ActiveStorage

Now, let’s configure ActiveStorage to work with Cloudflare R2.

You need to update your config/storage.yml file to set up both public and private storage services.

Configuration for ActiveStorage (config/storage.yml)

Here’s how to configure Cloudflare R2 with your custom domain for public and private file storage:

cloudflare_r2_public:
  service: S3
  access_key_id: <%= CLOUDFLARE_R2_CONFIG[:access_key_id] %>
  secret_access_key: <%= CLOUDFLARE_R2_CONFIG[:secret_access_key] %>
  region: <%= CLOUDFLARE_R2_CONFIG[:region] %>
  bucket: <%= CLOUDFLARE_R2_CONFIG[:public_bucket] %>
  endpoint: <%= CLOUDFLARE_R2_CONFIG[:endpoint] %>
  public: true
  upload:
    public_url: <%= CLOUDFLARE_R2_CONFIG[:public_url] %>
    acl: 'public-read'

cloudflare_r2_private:
  service: S3
  access_key_id: <%= CLOUDFLARE_R2_CONFIG[:access_key_id] %>
  secret_access_key: <%= CLOUDFLARE_R2_CONFIG[:secret_access_key] %>
  region: <%= CLOUDFLARE_R2_CONFIG[:region] %>
  bucket: <%= CLOUDFLARE_R2_CONFIG[:private_bucket] %>
  endpoint: <%= CLOUDFLARE_R2_CONFIG[:endpoint] %>
  public: false
  upload:
    acl: 'private'

Update the Model

Next, update your model to attach files using the configured services for both public and private file storage.

Model (app/models/organization.rb)

class Organization < ApplicationRecord
  has_one_attached :file, service: :cloudflare_r2_public
  has_one_attached :private_file, service: :cloudflare_r2_private
end

In this model:

  • The file is attached using the cloudflare_r2_public service, making it publicly accessible, with your own domain
  • The private_file is attached using the cloudflare_r2_private service, keeping it private.

Conclusion

By following the steps outlined in this guide, you’ve successfully integrated Cloudflare R2 with Ruby on Rails 8 for both public and private file storage. With this setup, you can store and manage files with Cloudflare’s scalable object storage, leveraging your own custom domain for public URLs.

For detailed installation steps, including setting up Cloudflare R2 with ActiveStorage, check out the installation guide at How to Integrate Cloudflare R2 with Ruby on Rails 8 for Public and Private File Storage.

Yes, that was all for this post, my philosophy Learn – Develop – Share is the belief that knowledge grows as we explore and build together. Stay tuned and may the force be with you 🤟

Leave a Reply

Your email address will not be published. Required fields are marked *