File uploads in Rails: Active Storage, Shrine and CarrierWave

Avatars, invoices, product photos: attachments look simple until you need cloud storage, thumbnails, direct uploads and validation. Rails has handled this itself since 5.2, but the two leading gems remain good choices for particular needs.
Release figures are from RubyGems.org, October 2026.
Active Storage
Active Storage, part of Rails since version 5.2, attaches files to Active Record models through two tables, active_storage_blobs and active_storage_attachments, so you do not add columns to your own models:
class User < ApplicationRecord
has_one_attached :avatar do |attachable|
attachable.variant :thumb, resize_to_limit: [200, 200]
end
has_many_attached :documents
end
Files are stored through configurable services: local disk, Amazon S3 and S3-compatible services, or Google Cloud Storage, with a mirror service for migrations. Direct uploads let the browser send files straight to the storage service, which keeps large uploads out of your application servers. Variants (resized or converted images) are produced on demand by the image_processing gem, using libvips by default in new applications, and previews can be generated for PDFs and videos with the right system tools installed.
The Active Storage Overview guide is thorough and worth reading end to end.
Limits to know: Active Storage does not validate content type or size out of the box; you need your own validation or a gem such as active_storage_validations. Its polymorphic tables make some queries less direct than a column on the model. And variants are processed lazily on first request unless you configure them to be processed in advance.
Choose it when you are starting a new application and your needs are typical. It is maintained with the framework and supported by most of the ecosystem, including admin panels and Action Text.
Shrine
Shrine (3.10.2, October 2026) is a toolkit built from small plugins. You enable only what you need: derivatives (processed versions), validation helpers, metadata extraction, backgrounding, presigned direct uploads, resumable uploads with tus, and more. It works with Active Record, Sequel or plain Ruby objects, and it stores attachment data in a single JSON column on your own table.
Shrine's documentation is excellent and explains the trade-offs of each approach to uploading, which makes it instructive even if you end up using Active Storage.
Choose it when you need fine control over the upload pipeline, want validation and processing as first-class concepts, or work outside Rails models.
CarrierWave
CarrierWave (3.1.4, August 2026) organises uploads in uploader classes that you mount on a model column. Versions are defined in the uploader, and storage goes through the local filesystem or Fog-based cloud providers. It predates Active Storage, remains maintained and is common in applications started in the 2010s.
Choose it when you maintain an application that already uses it. For new work, Active Storage or Shrine are more usual starting points.
Paperclip and its fork
Paperclip was the dominant attachment library of the Rails 3 era. Thoughtbot deprecated it in 2018 and recommended Active Storage for new projects; its last release, 6.1.0, dates from July 2018. A community fork, kt-paperclip, continues to publish releases (8.0.1 in October 2026) for applications that cannot migrate yet. Our note on Paperclip covers its history and the migration paths.
Older still, attachment_fu was the standard plugin of the Rails 2 years; it is long abandoned.
Security and operations checklist
- Validate content type and size on the server, using the detected type rather than the file extension or the browser's claim. Active Storage identifies content types with the Marcel gem when it analyses the blob.
- Keep image libraries patched. libvips and ImageMagick parse untrusted files; their security updates matter as much as your gems'. libvips is generally the safer and faster default.
- Serve private files through expiring URLs. Active Storage's default redirect mode generates signed, short-lived service URLs; public buckets should hold only public files.
- Process in the background anything slower than a thumbnail, using your job backend.
- Plan for orphans. Abandoned direct uploads leave unattached blobs; schedule a cleanup task.
Summary
| Library | Data model | Strength |
|---|---|---|
| Active Storage | Two shared tables | Built in, widely supported |
| Shrine | JSON column on your table | Modular, explicit pipeline |
| CarrierWave | String column plus uploader class | Mature, familiar in older apps |
| kt-paperclip | Columns on your table | Bridge for legacy Paperclip apps |
Plugin historiesPaperclip
What Paperclip did, why thoughtbot deprecated it in 2018, the kt-paperclip fork, and how to migrate Paperclip attachments to Active Storage or Shrine.
Category guidesBackground jobs
How to choose a background job backend for Rails: Active Job, Solid Queue (the Rails 8 default), Sidekiq, GoodJob, delayed_job and Resque, and what replaced Workling and Spawn.
Category guidesAdmin panels
How ActiveAdmin, Avo, Administrate, RailsAdmin and Madmin approach back-office interfaces for Rails, their dependencies and licences, and when to build your own admin.