Build on Rails
Build the admin side of your Rails LMS
Learners take courses in the app you build. Your team needs the side behind it: courses and lessons in the right order, enrollments to fix, progress to check, and certificates to issue. With Avo, the admin of your Ruby on Rails LMS runs inside the same app, on the course and learner models you already have.
250+ paying companies · 2.7M downloads of the avo gem
Course
title
instructor_id
published
Enrollment
status
progress
access_ends_on
LessonCompletion
lesson_id
completed_at
The enrollment carries the learner's story: which course, how far, and until when. Completions underneath it are what the progress bar is made of.
starting from scratch
What building an LMS admin from scratch actually costs
The lesson player is what learners see and what the roadmap is about. The LMS admin is what instructors and student support open every day, and it is rarely planned for.
-
01
Course structure is a tree, not a table
A course has modules, a module has lessons, and the order matters. Editing that through a flat list or a seed file means instructors ask a developer every time a lesson moves up one slot.
-
02
Progress questions arrive daily
Why does this learner show 40 percent when they finished the module? Who has not opened the course since enrolling? Answering means joining enrollments, lesson completions, and quiz attempts, and without a screen for it every question becomes a SQL query.
-
03
Enrollment fixes cannot wait for a deploy
A company bought ten seats and needs three swapped, a learner paid twice, someone needs a week's extension on an access window. Each one is a small change to a record, and each one sits in a queue when only engineers can make it.
-
04
Instructors should see their own courses
A guest instructor needs to edit their course and see their learners, not the whole catalog or anyone's payment details. Writing that rule into every generated screen is where an LMS admin leaks.
the Avo version
Your models are already the hard part. Build the rest on Avo.
Your LMS already models the hard part: Course, Lesson, Enrollment, LessonCompletion, Certificate. Describe a resource for each and Avo renders the catalog, the learner records, and the progress views your team works from, on your real tables. The lesson player, quizzes, and course pages your learners use stay the app you build; Avo is where your team runs it. For the wider picture of running a school or learning product, see Avo for education.
Lessons in the order instructors want
A has_many field shows each course's modules and lessons on the course screen, and Record Reordering lets an instructor move a lesson up, down, or to the top without touching a position column by hand.
Lesson content edited where it lives
Rich text, video URLs, and downloadable files are fields on the Lesson resource, edited in place by instructors. Your validations still run, so a lesson without a title or a broken attachment never reaches a learner.
Progress you can read at a glance
A progress_bar field turns an enrollment's completion into a bar on every row, and Dynamic Filters narrow to learners below 20 percent, enrolled more than a month ago, in one course. Student support answers the progress question without a query.
Enrollment fixes and certificates as actions
Extend access, move a learner to another cohort, or reissue a certificate with an action that calls the code your LMS already uses. Avo does not generate the certificate PDF; your app does, and the action makes that a button support can press.
Instructors scoped to their own courses
With Authorization, a Pundit policy scope shows each instructor their courses and their learners, while admins see the whole catalog. Payment fields stay hidden for every role but finance, on every screen and every action.
Completion numbers from the same database
Enrollments this month, completion rate per course, and certificates issued on dashboards built from your queries, so the course team sees which courses learners finish and which they abandon.
An Enrollment resource with progress and the actions support runs, the whole file:
class Avo::Resources::Enrollment < Avo::BaseResource
self.title = :reference
self.includes = [:learner, :course]
self.search = {
query: -> { query.joins(:learner).where("learners.email ILIKE ?", "%#{q}%") }
}
def fields
field :id, as: :id
field :learner, as: :belongs_to, searchable: true
field :course, as: :belongs_to
field :status, as: :badge, options: {
info: :active,
success: :completed,
warning: :paused,
neutral: :expired
}
field :progress, as: :progress_bar, display_value: true, value_suffix: "%"
field :enrolled_at, as: :date_time, sortable: true
field :access_ends_on, as: :date
field :lesson_completions, as: :has_many
field :certificate, as: :has_one
end
def actions
action Avo::Actions::ExtendAccess
action Avo::Actions::ReissueCertificate
end
end
the timeline
From first resource to the team running on it
Add the avo gem and generate resources for Course, Lesson, and Enrollment. Your team opens any learner and sees what they are enrolled in and how far they got.
Put lessons in order with Record Reordering and add the progress bar to enrollments. Instructors stop sending lesson changes to engineering.
Write the instructor policy so each one sees their own courses, add the Extend access action, and ship it behind your existing login.
Instructors edit their own courses, support handles enrollments and certificates, and the LMS admin is a set of resource files your agents extend when you add quizzes or cohorts.
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 a brand-new Ruby on Rails LMS admin with Avo 4 (https://avohq.io, docs at https://docs.avohq.io/4.0/) as the team-facing UI. There is no app and no schema yet. The learner-facing course pages and lesson player are out of scope for this prompt: build the data model and the admin only.
1. Create the application with rails new using PostgreSQL. Add sign-in with Rails' built-in authentication generator (bin/rails generate authentication) so staff and instructors have real accounts. Add the avo gem, bundle, run bin/rails generate avo:install, and mount Avo at /avo behind that authentication.
2. Design the data model from first principles before writing a migration. An LMS has to represent courses and the ordered lessons inside them (decide whether modules or sections earn their own table), the learners, their enrollments with an access window and a status, lesson-by-lesson completion, and the certificates issued on completion. Decide how ordering is stored (a position column suits acts_as_list), where progress lives, and how instructors relate to courses. Write the plan down, then generate migrations and models.
3. Generate an Avo resource per model with bin/rails generate avo:resource (https://docs.avohq.io/4.0/resources.html). Set self.title, list associations in self.includes, and give self.search a query over learner email and course title. Match fields to columns: trix for lesson bodies, file for downloads, badge for enrollment status, progress_bar for completion percentage, date for access windows, belongs_to and has_many so a course, its lessons, its enrollments, and each learner render as linked screens.
4. Install the Record Reordering add-on (https://avohq.io/addons/record-reordering) and configure self.ordering on the lesson resource so instructors move lessons up and down from the Index view.
5. Turn the LMS verbs into Avo Actions: enroll a learner, extend access with a new end date, move a learner to another course run, and issue or reissue a certificate. Put the real behavior in model methods or small service objects, with certificate generation in a service that attaches a PDF to a Certificate record, and have each action call them.
6. Authorize with Pundit policies for three roles: admin (everything), instructor (their own courses, lessons, and learners only, no payment fields), and support (enrollments and certificates). Gate the actions by role (https://docs.avohq.io/4.0/authorization.html). Add a dashboard with enrollments this month, completion rate per course, and certificates issued (https://docs.avohq.io/4.0/dashboards.html).
7. Seed realistic data: two instructors with a course each, lessons in order, forty learners with enrollments at different progress levels, some completions and certificates, and a few expired enrollments, so every list and dashboard card has something to show.
8. Boot the app, sign in as an instructor, open /avo, confirm you see only your course, move a lesson to the top, then sign in as support and extend one learner's access with your action.
start today
Stand up an LMS admin this week
You pay per app, with unlimited users, so the whole team works from Avo 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
Avo by industry
Avo for your team
Frequently asked questions
- Can I build an LMS with Ruby on Rails?
- Yes. A learning management system is courses, lessons, enrollments, progress, and certificates, which map onto Active Record models and associations, and Rails handles the learner-facing pages, video, and background jobs well. The admin side is where teams lose weeks: ordering lessons, fixing enrollments, and answering progress questions. Avo builds that side on your models, so your engineers stay on the learning experience.
- Is there an open-source LMS built on Rails?
- Yes. Canvas LMS and Pupilfirst are open-source LMS applications built on Ruby on Rails. They suit a school that wants a complete LMS and is happy to run its schema and conventions. If your course platform is your own product, with its own models, pricing, and learner experience, build the admin on those models with Avo instead of adopting a second application.
- Does Avo build the course pages my learners see?
- No. Avo is the admin your team uses, not the learner-facing LMS. The lesson player, quizzes, course pages, and learner dashboard are your Rails app. Avo mounts inside that app and gives instructors, student support, and admins the screens to run it, on the same database, so there is nothing to sync.
- Can instructors manage only their own courses?
- Yes. A Pundit policy scope decides which courses and learners each instructor loads, and the same policy decides which fields they see and which actions they can run. With the Authorization add-on those rules hold on every screen, so a guest instructor never sees another course's learners or anyone's payments.
- How do we track learner progress and completions?
- Store completions as their own records, one per learner and lesson, and keep a progress percentage on the enrollment that your app updates. Avo renders the percentage as a progress bar, lists the completions on the enrollment, and filters to learners who stalled. See the resources docs for the field types.
- Can Avo issue certificates?
- Avo runs the action; your app makes the certificate. Write the certificate generation once in your LMS, then expose Issue and Reissue as Avo actions so support can send one from the enrollment screen. The certificate is a record with an attached file, so it shows up on the learner's enrollment like anything else.
- Who runs a course platform on Avo today?
- BBC Maestro manages the course catalog of its online course platform with Avo. Avo Community is free and open source, and paid plans are priced per app with unlimited users, so adding instructors and support staff does not change the bill.