Plugin histories

Paperclip: Rails attachments before Active Storage

Plugin histories · · Published 2026-10-24

A tangle of silver paper clips filling the frame
A tangle of steel paper clips.

For most of the 2010s, adding an avatar to a Rails model meant one line: has_attached_file :avatar. Paperclip was the default answer to file uploads until Rails grew its own. Here is where it stands, and how to move on.

What Paperclip did

Paperclip, published by thoughtbot, stored attachment metadata in columns on your own table and processed images with ImageMagick:

class User < ActiveRecord::Base
  has_attached_file :avatar,
    styles: { medium: "300x300>", thumb: "100x100#" },
    default_url: "/images/missing.png"
  validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\z/
end

A migration added four columns per attachment: avatar_file_name, avatar_content_type, avatar_file_size and avatar_updated_at. Each "style" was generated when the file was uploaded, using ImageMagick geometry strings, and stored on the local filesystem or Amazon S3. In the view, user.avatar.url(:thumb) returned the right path.

The design was simple to understand and easy to query, which explains its popularity. Its weaknesses grew with time: styles were processed synchronously during the request unless you added a gem such as delayed_paperclip, adding a new style meant reprocessing every file, and content-type spoofing became a security concern, which is why later versions required an explicit content-type validation (or an explicit opt-out) on every attachment.

The Rails 3 transition

When Rails 3 arrived in 2010, Paperclip, like most plugins, had to adapt to Bundler, the new Railtie system and Active Record's new query interface. For a while users ran a dedicated Rails 3 branch or community forks installed from Git, before released gem versions caught up. Paperclip then followed Rails through versions 4 and 5, and remained one of the most widely used attachment libraries until Active Storage.

Deprecation

In 2018, after Active Storage shipped with Rails 5.2, thoughtbot announced that Paperclip was deprecated and recommended Active Storage for new projects. The repository carries a deprecation notice and a migration guide. According to RubyGems.org, its last release is 6.1.0, from July 2018. No fixes, including security fixes, are published for it any more.

A community fork, kt-paperclip, keeps the same API working on newer Ruby and Rails versions and is still released (8.0.1 in October 2026, per RubyGems.org). It is a sensible bridge for an application that cannot migrate yet, but it is a fork with its own, smaller maintainer base; treat it as a way to buy time rather than a long-term home.

Migrating to Active Storage

Active Storage stores attachments in two shared tables rather than in columns on your model, so migration involves copying data, not just changing a line. The usual approach:

  1. Add Active Storage with bin/rails active_storage:install and configure a service that points at your existing bucket or a new one.
  2. Declare the new attachment under a temporary name (for example has_one_attached :avatar_file) next to the Paperclip one.
  3. Backfill in a background job: for each record, either create an ActiveStorage::Blob pointing at the existing object key and checksum, or download and re-attach the original file. Pointing at existing keys avoids copying data but requires care with key formats; re-attaching is slower but simpler.
  4. Switch reads to the new attachment, replace Paperclip styles with Active Storage variants, and check every view and serializer.
  5. Remove Paperclip and its columns once nothing reads them.

Paperclip's own migration guide in its repository describes the data mapping in detail, and the Active Storage Overview covers variants, direct uploads and services.

Or Shrine

If you prefer to keep attachment data on your own table, Shrine stores it in a single JSON column and offers explicit derivatives, validation and backgrounding. Its documentation includes a guide for migrating from Paperclip, and it can be a smoother path for applications that rely heavily on Paperclip's per-model columns. Our file uploads guide compares the options.

Checklist for legacy Paperclip apps