
Migrating from Custom Rails to Jumpstart Pro
Upgrade your custom Rails app to Jumpstart Pro for built-in multitenancy, payments, and authentication that just works.
Prerequisites
- •Familiarity with Ruby on Rails conventions
- •Understanding of your current Rails application structure
- •Basic knowledge of database migrations
- •Active Jumpstart Pro license
Migrating from Custom Rails to Jumpstart Pro
If you've built a custom Rails SaaS and want to standardize on a proven foundation, Jumpstart Pro offers authentication, payments, multitenancy, and admin features out of the box. This guide walks you through migrating your existing app.
Why Migrate?#
You might want to migrate if you need:
- Battle-tested authentication (Devise with social logins)
- Stripe/Paddle/Braintree payments already wired up
- Account/team multitenancy with invitations
- Admin dashboard with Administrate
- Hotwire/Turbo integration done right
- Regular security updates
Stay with custom Rails if:
- Your app is very simple (no payments, no teams)
- You have highly custom auth requirements
- You're too deep into a different payment system
- Migration cost outweighs benefits
Migration Overview#
| Aspect | Custom Rails | Jumpstart Pro |
|---|---|---|
| Auth | Custom/Devise | Devise + OmniAuth |
| Payments | Custom/None | Pay gem (Stripe, Paddle, Braintree) |
| Multitenancy | Custom/None | Acts as Tenant + Account model |
| Admin | Custom/None | Administrate |
| Frontend | Varies | Hotwire, Tailwind CSS |
| Background Jobs | Varies | Sidekiq |
Step 1: Set Up Jumpstart Pro#
# Clone your Jumpstart Pro repo
git clone git@github.com:your-username/jumpstart-pro.git my-app-v2
cd my-app-v2
# Install dependencies
bundle install
yarn install
# Set up database
rails db:create db:migrate
# Start the server to verify it works
bin/dev
Step 2: Plan Your Data Migration#
Inventory Your Models#
# List your current models
ls -la app/models/
# Generate a model dependency graph (optional)
rails erd
Map to Jumpstart Pro Conventions#
| Your Model | Jumpstart Pro Equivalent |
|---|---|
User | User (keep, but update) |
Team/Organization | Account |
Membership | AccountUser |
Subscription | Pay::Subscription |
Plan | Plan |
| Custom models | Copy directly |
Step 3: Migrate User Data#
Jumpstart Pro uses Devise with additional fields:
Your Current User Model#
# Your app/models/user.rb
class User < ApplicationRecord
has_secure_password # or devise
has_many :projects
has_many :subscriptions
end
Jumpstart Pro User Model#
# Jumpstart Pro app/models/user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:confirmable, :lockable, :trackable, :omniauthable
has_many :account_users, dependent: :destroy
has_many :accounts, through: :account_users
has_one :personal_account, -> { where(personal: true) }, class_name: "Account"
# Pay gem for payments
pay_customer
end
Migration Script#
# lib/tasks/migrate_users.rake
namespace :migrate do
desc "Migrate users from old app"
task users: :environment do
# Connect to old database
OldUser = Class.new(ApplicationRecord) do
self.table_name = 'old_users'
establish_connection(
adapter: 'postgresql',
database: 'old_app_production'
)
end
OldUser.find_each do |old_user|
# Create user in Jumpstart Pro
user = User.new(
email: old_user.email,
password: SecureRandom.hex(16), # They'll need to reset
first_name: old_user.name&.split(' ')&.first,
last_name: old_user.name&.split(' ')&.last,
confirmed_at: old_user.email_verified_at || Time.current
)
# Skip validation for migrated users
user.save!(validate: false)
# Create personal account
account = user.create_personal_account!(name: "Personal")
# If they had a subscription, we'll handle that separately
puts "Migrated user: #{user.email}"
end
puts "Migration complete!"
end
end
Step 4: Migrate Teams/Organizations to Accounts#
Jumpstart Pro uses Account for multitenancy:
# lib/tasks/migrate_teams.rake
namespace :migrate do
desc "Migrate teams to accounts"
task teams: :environment do
OldTeam = Class.new(ApplicationRecord) do
self.table_name = 'old_teams'
establish_connection(...)
end
OldTeam.find_each do |old_team|
# Find the owner
owner = User.find_by(email: old_team.owner_email)
next unless owner
# Create account
account = Account.create!(
name: old_team.name,
owner: owner,
personal: false
)
# Add team members
old_team.members.each do |member|
user = User.find_by(email: member.email)
next unless user
AccountUser.create!(
account: account,
user: user,
roles: { admin: member.role == 'admin' }
)
end
puts "Migrated team: #{account.name}"
end
end
end
Step 5: Migrate Payments#
Jumpstart Pro uses the Pay gem for payments:
If You Had Custom Stripe Integration#
# lib/tasks/migrate_subscriptions.rake
namespace :migrate do
desc "Migrate Stripe subscriptions"
task subscriptions: :environment do
OldSubscription = Class.new(ApplicationRecord) do
self.table_name = 'old_subscriptions'
establish_connection(...)
end
OldSubscription.where(status: 'active').find_each do |old_sub|
user = User.find_by(email: old_sub.user_email)
next unless user
# Pay gem creates Pay::Customer records automatically
# We need to sync the existing Stripe data
# Create Pay::Customer
pay_customer = Pay::Customer.find_or_create_by!(
owner: user,
processor: :stripe,
processor_id: old_sub.stripe_customer_id
)
# Create Pay::Subscription
Pay::Subscription.create!(
customer: pay_customer,
processor: :stripe,
processor_id: old_sub.stripe_subscription_id,
processor_plan: old_sub.stripe_price_id,
status: old_sub.status,
current_period_start: old_sub.current_period_start,
current_period_end: old_sub.current_period_end
)
puts "Migrated subscription for: #{user.email}"
end
end
end
Update Stripe Webhooks#
Point your Stripe webhook to the new endpoint:
https://your-new-app.com/pay/webhooks/stripe
Step 6: Migrate Custom Models#
Copy your business models and update relationships:
Before (Custom Rails)#
# app/models/project.rb
class Project < ApplicationRecord
belongs_to :user
belongs_to :team, optional: true
end
After (Jumpstart Pro)#
# app/models/project.rb
class Project < ApplicationRecord
belongs_to :account # Use account instead of team
belongs_to :user, optional: true # Creator, if needed
# Acts as Tenant scoping
acts_as_tenant :account
end
Run the migration#
# db/migrate/xxx_update_projects_for_jumpstart.rb
class UpdateProjectsForJumpstart < ActiveRecord::Migration[7.0]
def change
# Add account reference
add_reference :projects, :account, foreign_key: true
# Migrate data
reversible do |dir|
dir.up do
Project.find_each do |project|
# Find account from old team or user
if project.team_id
account = Account.find_by(old_team_id: project.team_id)
else
account = User.find(project.user_id).personal_account
end
project.update!(account: account)
end
end
end
# Remove old columns
remove_column :projects, :team_id
end
end
Step 7: Update Controllers#
Jumpstart Pro provides helper methods for current account:
Before#
class ProjectsController < ApplicationController
before_action :authenticate_user!
def index
@projects = current_user.team.projects
end
end
After#
class ProjectsController < ApplicationController
before_action :authenticate_user!
def index
# current_account is provided by Jumpstart Pro
@projects = current_account.projects
end
end
Step 8: Update Views#
Jumpstart Pro uses Tailwind CSS and ViewComponent:
Migrate to Tailwind#
If your old app used Bootstrap or custom CSS:
<!-- Before (Bootstrap) -->
<div class="card">
<div class="card-body">
<h5 class="card-title"><%= @project.name %></h5>
</div>
</div>
<!-- After (Tailwind) -->
<div class="bg-white rounded-lg shadow p-6">
<h5 class="text-lg font-semibold text-gray-900"><%= @project.name %></h5>
</div>
Use Jumpstart Pro Components#
<!-- Jumpstart Pro provides styled components -->
<%= render "shared/card" do %>
<h5 class="text-lg font-semibold"><%= @project.name %></h5>
<% end %>
Step 9: Set Up Background Jobs#
Jumpstart Pro uses Sidekiq:
# Move your jobs to app/jobs/
# Update ActiveJob queue adapter if needed
# config/application.rb
config.active_job.queue_adapter = :sidekiq
Testing Checklist#
- User sign up and sign in
- Social logins (Google, GitHub, etc.)
- Password reset
- Email confirmation
- Account creation and switching
- Team invitations
- Subscription checkout
- Subscription management
- Webhooks receiving
- All custom features work
- Background jobs process
Common Challenges#
1. Password Migration#
Problem: Can't migrate hashed passwords between different systems.
Solution: Force password reset for all users:
# Send reset emails after migration
User.find_each do |user|
user.send_reset_password_instructions
end
2. Different Devise Configuration#
Problem: Your Devise setup differs from Jumpstart Pro's.
Solution: Carefully merge configurations in config/initializers/devise.rb.
3. Tenancy Scoping#
Problem: Queries aren't scoped to current account.
Solution: Ensure all models have acts_as_tenant :account and you're setting current_account properly.
Timeline Estimate#
| Phase | Estimated Time |
|---|---|
| Setup & Planning | 2 hours |
| User Migration | 3 hours |
| Team/Account Migration | 3 hours |
| Payment Migration | 4 hours |
| Custom Model Migration | 4 hours |
| View/Controller Updates | 2 hours |
| Testing | 2 hours |
| Total | ~20 hours |
Conclusion#
Migrating to Jumpstart Pro gives you a solid, maintained foundation for your Rails SaaS. The investment pays off in reduced maintenance, security updates, and battle-tested patterns. Focus your energy on what makes your product unique, not on reinventing authentication and payments.
Not sure which boilerplate to choose?
Take our 2-minute quiz and get personalized recommendations.
Take the Quiz