Build on Rails

Build a CRM on Rails

Point Avo at the contacts, companies, and deals you already have in Postgres and get a working pipeline your reps can run, without hand-building another CRUD app.

250+ paying companies · 2.7M downloads of the avo gem

01

01 · the hand-built version

What building a CRM by hand actually costs

A CRM is deceptively simple until real reps use it. The parts that hurt are never the first contact form, they are everything that shows up in week two.

  • 01

    The pipeline never stays a static list

    You start with a stage enum and a table. Then someone wants to reorder stages, filter to their own open deals, and see what is closing this month, and you are suddenly building views instead of selling.

  • 02

    Every rep wants one more field

    Next follow-up date, deal source, lead score. Each request is a migration, a form tweak, and an edit to three views. The CRM becomes a queue of small changes nobody has time for.

  • 03

    The activity log is a project of its own

    Notes, calls, and emails need a timeline against every contact and deal. Roll your own polymorphic activity feed and you now own a feature that has nothing to do with your actual product.

  • 04

    Reps should not see each other's book

    Managers see everything, reps see their own accounts. Get that wrong and you have a data leak. Get it right by hand and you have current_user checks scattered through every controller.

02

02 · the Avo version

Your models are already the hard part. Avo is the rest.

A CRM is mostly screens over relationships, and your app already has the relationships. Describe a resource for each model, Contact, Company, Deal, and Avo renders the Index view, filters, forms, and detail screens on top of your real tables. It runs inside your Rails app, so the pipeline sits next to the data instead of syncing to a third-party CRM.

Your real models, not a parallel schema

One resource file per model gives you full CRUD on the contacts and deals already in your database. Declare fields with the field DSL and Avo handles the Index, Show, and form views. See https://docs.avohq.io/4.0/resources.html.

The relationships you already modeled

A belongs_to field links a contact to its company and a deal to its owner, a has_many field gives you the deals and notes hanging off each contact. The associations you wrote in Active Record become navigable screens.

Slice the pipeline without new views

Filters and scopes let a rep narrow to open deals or overdue follow-ups. Point a scope at a model scope you already trust, like Deal.closing_this_month, so the query logic lives in one place.

One-click on the work reps actually do

Actions turn 'mark this deal won', 'reassign owner', or 'log a call' into buttons that run against one record or a whole selection at once, instead of a rake task or a console session.

Each rep sees their own book

Authorization uses plain Pundit-style policies, so reps see the accounts they own and managers see everything. Your rules live in one policy object, not sprinkled across controllers. See https://docs.avohq.io/4.0/authorization.html.

A pipeline dashboard on day one

Add metric and chart cards for pipeline value by stage and deals closing this month, so the team opens Avo to a real read on the quarter instead of a blank CRUD table.

A first Contact resource, the whole file:

class Avo::Resources::Contact < Avo::BaseResource
  self.title = :full_name
  self.includes = [:company, :owner]
  self.search = {
    query: -> { query.where("full_name ILIKE ?", "%#{q}%") }
  }

  def fields
    field :id, as: :id
    field :full_name, as: :text, required: true
    field :email, as: :text
    field :phone, as: :text
    field :company, as: :belongs_to
    field :owner, as: :belongs_to
    field :stage, as: :select, enum: ::Contact.stages
    field :last_contacted_at, as: :date_time, sortable: true
    field :deals, as: :has_many
    field :notes, as: :has_many
  end
end
03

03 · the timeline

From first resource to the team running on it

Hour one

Add the avo gem, mount it, and generate resources for Contact, Company, and Deal. You are clicking through your real accounts and editing a live deal before lunch.

The first afternoon

Add a stage filter, a 'Mark won' action, and a dashboard card for pipeline value. The CRM starts behaving like a tool built for reps, not a generic admin.

Day one

Wire it behind your existing Devise login and a Pundit policy so reps only see their own accounts. Ship it to production and let the team work real deals in it.

By the end of the week

Reps run their pipeline from Avo. The tracking spreadsheet and the half-finished internal admin get retired, and you are maintaining one app instead of two.

Prefer to prompt?

Hand this to Claude Code, Cursor, or whatever agent you build with, and review what it ships like any other pull request.

Build me a brand-new Ruby on Rails CRM from scratch with Avo 4 as the management UI (https://avohq.io, docs at https://docs.avohq.io/4.0/). I have no app and no schema yet, so scaffold the whole thing.

1. Generate the application with rails new using PostgreSQL, then set up sign-in with Rails' built-in authentication generator (bin/rails generate authentication) so there is a real User behind the login. Add the avo gem to the Gemfile, bundle, run bin/rails generate avo:install, and mount Avo at /avo so it sits behind that authentication and only signed-in users reach it.
2. Work out the data model before touching a migration. A CRM lives or dies on how it represents the people and accounts a team sells to, the deals moving through a pipeline, and the running history of calls, notes, and emails logged against them. Decide from first principles which entities this genuinely needs, whether a deal's pipeline position is best as an enum on the record or a stages table of its own, and how everything associates: who a deal belongs to, which contacts sit under a company, where an activity attaches. Write that plan down, then generate the migrations and models to match. Treat those CRM concepts as ground the design has to cover, not a schema to transcribe.
3. Generate an Avo resource for each model with bin/rails generate avo:resource (https://docs.avohq.io/4.0/resources.html). Set self.title to the label a rep would recognize, list the associated tables in self.includes so the Index view does not fire a query per row, and give self.search a query over the columns someone would actually type, like a contact name or company. Match every field to the column you chose: text and text/textarea for names and notes, date_time for timestamps, select backed by the enum for a deal stage or status, belongs_to and has_many for the associations so contacts, deals, and their activity render as linked screens.
4. Turn the pipeline verbs into Avo Actions: marking a deal won or lost, reassigning its owner, logging an interaction against a contact. Since the app is new, put the real behavior in model methods or small service objects and have each action call into that, so the business logic lives in one testable place instead of inside the action. Add filters and scopes for the slices reps live in, such as open deals, my accounts, or overdue follow-ups, and point each scope at a model scope so the query is defined once.
5. Add a dashboard with metric and chart cards for what a sales team watches day to day, pipeline value by stage and deals closing this month among them, built from the models you settled on (https://docs.avohq.io/4.0/dashboards.html).
6. Authorize with Pundit policies so a rep only sees and edits the accounts and deals they own while a manager sees the whole book, and the same rules gate the actions so a rep cannot reassign a deal that is not theirs (https://docs.avohq.io/4.0/authorization.html).
7. Seed realistic sample data: a handful of users across both roles, companies with contacts under them, and deals spread over every stage with a few logged activities, so every Index view, filter, and dashboard card has something real to show.
8. Boot the app, sign in, open /avo, and confirm the loop works end to end: browse to a company, open one of its deals, move it to a later stage with your Mark won action, and watch the pipeline dashboard update.

04 · start today

Stand up a CRM this week

Avo is per app with unlimited users, so the whole team works from it without the bill scaling with headcount. When you hit an edge Avo doesn't cover, you drop to plain Rails instead of filing a feature request.

More things teams run from their Rails app

Frequently asked questions

Can I build a CRM with Ruby on Rails?
Yes. A CRM is contacts, companies, deals, and a pipeline, which map cleanly onto Active Record models and associations. The work most teams underestimate is the admin layer on top: the filtered lists, the forms, the permissions, and the activity log. Avo generates that layer from your existing models so you write pipeline logic instead of CRUD screens.
What is the fastest way to build a CRM in Rails?
Model your contacts, companies, and deals as you normally would, then let Avo resources render the interface on top. You skip building index views, forms, search, and filters by hand, which is where most of the time goes. A working pipeline your reps can use is realistic on the first day rather than after a sprint.
Does Avo work with my existing Rails models?
Yes, that is the point. Avo reads your current Active Record models and associations, so a Contact that belongs_to a Company and has_many deals shows up as connected screens with no schema changes. You are adding an interface to the data you already have, not migrating to a new CRM.
Can I control which contacts each sales rep sees?
Yes. Avo uses Pundit-style policies, so you scope records to their owner and let managers see everything from one policy object. The same rules cover viewing, editing, and running actions, so a rep cannot reassign a deal they should not touch.
How is this different from Salesforce or HubSpot?
A hosted CRM lives outside your app, so your customer data leaves your database and every custom field or workflow is a config screen and an integration to maintain. Avo runs inside your Rails app on your infrastructure, next to the data. When you outgrow a screen you drop to plain Rails instead of filing a feature request.
Can I customize the pipeline stages and fields?
Yes. Stages are a normal enum or lookup table you control, and adding a field like lead source or next follow-up is one line in the resource plus a migration. Because it is your Rails code, there is no vendor limit on what a record can hold or how the pipeline is shaped.