# RailsFast Documentation
> Complete documentation for RailsFast - The AI coding template
## Documentation
- [🚀 Welcome! - RailsFast Docs](/)
- [Prerequisites](/getting-started-1-prerequisites/)
- [Quickstart (15 Minutes)](/getting-started-2-quickstart/)
- [Rebrand & customize](/getting-started-3-customization/)
- [Configure credentials](/getting-started-4-configuration/)
- [Developing your app](/getting-started-5-develop/)
- [Stripe, payments & plans](/features-0-payments/)
- [User authentication & login](/features-1-auth/)
- [Your new admin tools](/features-2-admin/)
- [Sending transactional emails](/features-3-emails/)
- [Background jobs & scheduled cron jobs](/features-4-background-jobs/)
- [Uptime and monitoring](/features-5-monitoring/)
- [SEO & Sitemaps](/features-6-seo/)
- [Analytics](/features-7-analytics/)
- [Caching](/features-9-caching/)
- [Attachments and cloud storage (S3-compatible)](/features-12-attachments/)
- [Abuse protection](/features-13-abuse-protection/)
- [Usage credits](/features-14-usage-credits/)
- [UI Overview](/ui-components-1-ui-overview/)
- [Social Proof](/ui-components-2-social-proof/)
- [Testimonials](/ui-components-3-testimonials/)
- [FAQ System](/ui-components-4-faq-system/)
- [Features](/ui-components-5-features/)
- [Call to action](/ui-components-6-cta/)
- [Pricing page](/ui-components-7-pricing/)
- [Other components](/ui-components-8-other/)
- [Customizing components](/ui-components-9-customizing/)
- [AI image generation](/ai-2-image-generation/)
- [LLMs and AI chats](/ai-3-llm-chat/)
- [AI, vector databases, and RAG](/ai-4-vectors-and-rag/)
- [How to get a 10/10 deliverability score for your emails](/guides-email-deliverability/)
- [Kamal - Configure a container registry](/guides-kamal-container-registry/)
- [Kamal - Configure a remote builder](/guides-kamal-remote-builder/)
- [Migrating Existing Project](/guides-migrating-existing-project/)
- [Deploy multiple Rails apps to the same server](/guides-multiple-apps/)
- [Updating RailsFast](/guides-updating/)
---
## Full Content
## 🚀 Welcome! - RailsFast Docs
Welcome to RailsFast!
You're only a few minutes away from putting your first RailsFast app in production!
But first of all: thanks for purchasing! You've made a great choice. This single decision could very well be the start of your own software business; or it could be the lever to finally take that idea you've been wanting to build into reality.
> [!TIP]
> 👉 You can go straight ahead and **[[getting-started/1-prerequisites|start building now]]**, or you can read below why I made this project.
---
---
",
subject: "Welcome!",
) do
h1 "Welcome aboard, #{recipient.name}!"
text "We're thrilled to have you join the community."
button "Go to Dashboard", user_dashboard_url(recipient)
sign
end
mail.deliver_now
```
You can customize all transactional emails however you want! Add animated GIFs, change the text, make new emails... make sure to read the [`goodmail`](https://github.com/rameerez/goodmail/) gem docs to know everything that's possible.
If you don't like `goodmail`, or if you'd like to use your own email templates, you can still use Rails' [`ActionMailer`](https://guides.rubyonrails.org/action_mailer_basics.html) -- `goodmail` is just an add-on, doesn't replace Action Mailer, you can keep writing emails with Action Mailer if that's what you like!
---
## Background jobs & scheduled cron jobs
RailsFast comes with a robust background job processing system using [Solid Queue](github.com/rails/solid_queue/), Rails 8's built-in job backend.
This is a fully-functional, production-ready background job system that allows you to:
- Offload long-running operations (like processing files, or running cleanup tasks) as background jobs to be performed asynchronously in the background whenever the system can
- Enqueue jobs to be performed later, in 5 minutes or any arbitrary amount of time
- Send emails / do things whithout blocking the current request and making the user feel wait time
- Handle file uploads and processing
- Schedule jobs to run at night or whenever you'd like (`cron`-like), very useful for admin and reporting tasks
- Handle retries automatically, retry jobs multiple times if they fail
> [!INFO]
> Something people don't usually expect is that this entire system doesn't have any dependency on Redis! You don't need any Redis process, instance or any in-memory store system to run this, it's all database-backed! (and very efficient in production!)
## Creating a background job
It's better you read the [`ActiveJob`](https://guides.rubyonrails.org/active_job_basics.html) docs, but in a nutshell, you generate a new job with:
```bash
rails generate job SendWelcomeEmail
```
Which creates the job file:
```ruby
# app/jobs/send_welcome_email_job.rb
class SendWelcomeEmailJob < ApplicationJob
queue_as :default
def perform(user)
UserMailer.welcome_email(user).deliver_now
end
end
```
And then you can either perform the job right now:
```ruby
SendWelcomeEmailJob.perform_later(current_user)
```
Or perform it later:
```ruby
# Perform in 5 minutes
SendWelcomeEmailJob.set(wait: 5.minutes).perform_later(current_user)
# Perform at specific time
SendWelcomeEmailJob.set(wait_until: Date.tomorrow.noon).perform_later(current_user)
```
## Schedule recurring jobs (`cron`-like)
You can schedule recurring jobs, which behave pretty much like `cron` behaves. To see all your recurring jobs and edit when they get triggered, open your `config/recurring.yml` file:
```yaml
production:
refresh_sitemap:
class: SitemapRefreshJob
queue: default
schedule: every day at 4am Europe/Lisbon
```
Refer to the [`ActiveJob`](https://guides.rubyonrails.org/active_job_basics.html) docs to learn how to handle job errors and retries.
## Web UI
As mentioned in the [[features/2-admn|Admin]] section, RailsFast comes with a dashboard to see all your background tasks, all your scheduled task and when they last ran, the output and result of each job run, etc. You can also discard and retry jobs manually. Access it as an admin user via `/admin/jobs`
---
## Uptime and monitoring
RailsFast comes preconfigured with the [`allgood`](https://github.com/rameerez/allgood) gem for health checks / smoke tests.
You can see all your current health checks by navigating to `/healthcheck`:
By default, we check things like whether the database is up and running, the server has enough disk and memory, etc. You can customize your health checks by editing your `config/allgood.rb` file. Check the [`allgood`](https://github.com/rameerez/allgood) docs to know everything you can add to check the health of your app!
The recommended use of this `/healthcheck` endpoint is to use it together with an uptime monitoring service like UptimeRobot. In UptimeRobot, you'd just add `https://YOURAPP.COM/healthcheck` as an URL to monitor every 5 minutes, and whenever any of the checks fail, you'd get a notification, a call, or a text, depending on what you have configured. It's very useful to quickly know if there's anything wrong with your app and fix it before any of your users notice!
---
## SEO & Sitemaps
RailsFast has been designed for SEO and discoverability from the ground up. Your project, immediately upon deployment, should score 100/100 on SEO and close to 100/100 on overall performance on [Google PageSpeed Insights](https://pagespeed.web.dev/):
Other than technical improvements and good technical architecture, your main two ways of affecting on-page SEO on your RailsFast app are: metadata and the sitemap.
## Metadata
We use the [`meta-tags`](github.com/kpumuk/meta-tags) gem to handle on-page metadata. Everything is already configured and you'll see examples throughout the codebase, but to make it explicit, you can set your page metadata inside views like this:
```erb
<% title "My page" %>
<% description "This is the SEO description for my page" %>
```
Or in the corresponding controller action:
```ruby
@page_title = "My page"
@page_description = "My SEO description"
```
You can also set your page OG card image like this:
```ruby
set_meta_tags og: { image: "https://example.com/image.jpg" }
```
## Sitemaps
Your app sitemap is available at `/sitemaps/sitemap.xml.gz`
You can configure what URLs go in the sitemap by editing your `config/sitemap.rb` file. You can dynamically add thousands of URLs by iterating over all your database records. We leverage the [`sitemaps-generator`](github.com/kjvarga/sitemap_generator) gem for this, read their docs to know what's possible!
You can also enable automatic sitemap discovery in your `robots.txt` file by uncommenting the sitemap line at the end of the file.
TODO: make sure Cloudflare is not blocking sitemaps / are readable by GSC
## Slugs
If you want to create SEO-friendly URLs, I recommend using the [`slugifiable`](https://github.com/rameerez/slugifiable) gem.
This will allow you to have semantic URLs like
```ruby
https://myapp.com/products/big-red-backpack
```
instead of
```ruby
https://myapp.com/products/14
```
The `slugifiable` gem does not come pre-configured by default in RailsFast because it's very app-dependent, but it should take you just ~5 minutes to set up!
---
## Analytics
If you're using an analytics service like Google Analytics, Umami, SimpleAnalytics, etc. you can add its tag to `app/views/layouts/html_head/_analytics.html.erb`, and this will achieve three things:
- It will add your analytics tag to all pages
- Without cluttering the main layout (`app/views/layouts/application.html.erb`)
- It's instantly reusable by other layouts in your app (like `app/views/layouts/devise.html.erb`) maintaining a single source of truth for all your analytics tags
---
## Caching
RailsFast comes with everything ready and configured so you can just cache database-intensive parts of your with just the keyword `cache`:
```ruby
<% @products.each do |product| %>
<% cache product do %>
<%= render product %>
<% end %>
<% end %>
```
Or, if you want to cache conditionally, you can do so with `cache_if`:
```ruby
<% cache_if admin?, product do %>
<%= render product %>
<% end %>
```
Learn everything you can do with caching in the official [Rails caching](https://guides.rubyonrails.org/caching_with_rails.html) docs
---
## Attachments and cloud storage (S3-compatible)
Uploading images and files is one of that features that looks trivial until you realize it's not. And mostly every app requires it: setting up user avatars is maybe one of the most common features. Thankfully, RailsFast comes with everything working out of the box, and you don't need to do mostly anything as long as you've configured your R2 credentials correctly in the [[getting-started/4-configuration|Configuration]] section.
In the demo app that ships with RailsFast, all users can upload and update their avatar. This is done by just one line of code in the `User` model:
```ruby
has_one_attached :avatar do |attachable|
attachable.variant :thumb, resize_to_limit: [100, 100], format: :webp
end
```
As you can see, you can indicate optional sizes and compression for better asset serving & on-page optimization. RailsFast comes with another two image sizes (variants) configured: `:small` and `:medium`, so you get an idea how to use it, but it's quite straightforward.
If you want to add image (or file) attatchments to any other model in your app, just copy what's already working for user avatars, and read the official [Active Storage](https://guides.rubyonrails.org/active_storage_overview.html) docs.
---
## Abuse protection
RailsFast ships with several measures you can use to protect against abuse and bots / DDoS / scrapers / bad actors.
## Cloudflare Turnstile (Captcha-like)
Cloudflare Turnstile is a widget, like the famous CAPTCHA, that validates whether the user is human or not. This is useful to prevent bots from automatically / massively submitting information to your app, for example.
As we saw in the auth section, all signup/login forms come already protected against abuse by Cloudflare Turnstile.
If you want to add Turnstile (Captcha-like) protection to any other form on any other page, you just need to add the `cloudflare_turnstile` element to your view:
```erb
<%= cloudflare_turnstile(theme: 'dark') %>
```
And then, in the corresponding controller action:
```ruby
before_action :validate_cloudflare_turnstile, only: [:your_action]
```
Everything is configured in RailsFast so that these two lines are enough to add a fully functional Turnstile validation to any form in your app.
We leverage the [`cloudflare-turnstile-rails`](https://github.com/vkononov/cloudflare-turnstile-rails) gem for this, in case you need more documentation in how to use it.
## Rate limiting
I recommend setting up any rate-limiting countermeasures in Cloudflare (it's more advanced and kills any request before it actually hits your Rails app), but in case you don't want to / can't do it, you still have a very solid and production-ready mechanisim to easily rate limit requests in your RailsFast apps.
Just add `rate_limit` to any controller:
```ruby
class DemoController < ApplicationController
rate_limit to: 3, within: 1.minute, by: -> { current_user.id }, only: :my_action
end
```
For example, the rule above will block users from submitting more than 3 requests per minute to the `my_action` endpoint.
Read more about `rate_limit` in the official [`RateLimiting`](https://api.rubyonrails.org/classes/ActionController/RateLimiting/ClassMethods.html) docs.
---
## Usage credits
RailsFast ships with a fully working usage credits system, so you can keep track of how many credits each of your users has, and allow them to spend credits on your app.
If you don't need this in your app, you can just ignore it! There's nothing else to do.
If you do want usage credits in your app, everything is ready to go! You just need to define how your users get credits and how much each credit-consuming operation costs in `usage_credits.rb`.
For example, you define how your users get credits like this:
```ruby
stripe_config = Rails.configuration.x.railsfast.dig(:stripe, :plans) || {}
subscription_plan :basic_plan do
stripe_price month: stripe_config.dig(:basic_plan, :monthly), year: stripe_config.dig(:basic_plan, :yearly)
gives 10_000.credits.every(:month)
end
```
And you define the cost of operations like this:
```ruby
operation :process_image do
costs 10.credits + 1.credit_per(:mb)
end
```
And then you can do this anywhere in your app:
```ruby
@user.spend_credits_on(:process_image) do
# Perform the actual operation here.
# No credits will be spent if this block fails.
end
```
Finally, to make everything work in both development and production, uncomment the credit fulfillment jobs from `config/recurring.yml`:
```yaml
# Uncomment this so usage_credits refills credits in production:
refill_credits:
class: UsageCredits::FulfillmentJob
queue: default
schedule: every 5 minutes
```
RailsFast uses the [`usage_credits`](https://github.com/rameerez/usage_credits/) gem for this, please refer to the gem README to learn everything you can do and how to fully define credit-consuming operations and credit-giving plans.
---
## UI Overview
RailsFast uses Tailwind 4.3+ for all its UI. The components are all Tailwind too, so if you're already familiar with it you'll feel right at home. You can do in RailsFast whatever you would usually do with any Tailwind project!
You can either write your own UI components from scratch using Tailwind, or use any UI components library like Tailwind Pro or Shadcn, or any other you are familiar with. Just bring your components and you're ready to go!
However, since RailsFast aims at providing you with a really fast speed to market, I thought it would be a good idea to include some very common and useful components you could just mix-and-match like LEGO blocks to quickly build marketing landing pages and UIs. I think that (as long as you already have the copy text ready) you could put together entire SEO-optimized, conversion-oriented marketing pages in 10 or 20 minutes, which I think it's extremely powerful.
You're not forced to use any RailsFast components, though: you can just ignore them if you'd rather use your own stuff. But I really think you'd love them, not only because they harness the full power of RailsFast, but because some of them (like the testimonials component) are so complete they could very well replace entire separate SaaS subscriptions that you'd need to achieve the same results. You can edit any component to tailor it to your needs and styles, too!
---
## Social Proof
The `social_proof` component lets you show social support to your app or product through a series of circle avatars.
Use it like:
```ruby
<%= render 'components/railsfast/marketing/social_proof',
avatars: [
{ image_url: "https://example.com/image.jpg", alt_text: "John Smith" },
],
rating: 4.9,
max_avatars: 5,
size: "size-10",
overlap: "-ml-3",
theme: "dark",
avatar_border: "border-white/90",
star_color: "text-white",
overflow_count: "2.5k",
trust_message: "2,500+ users love RailsFast" %>
```
`theme` can be:
- `light` (default)
- `dark`
Parameters:
- `avatars`: array of `{ image_url:, alt_text: }` (defaults to demo avatars)
- `max_avatars`: max number of avatars to show (default: `5`)
- `rating`: rating number (default: `5.0`)
- `overflow_count`: shows a `+X` overflow pill (only shown when explicitly provided)
- `trust_count`: trust count text (default: `"1,000+"`)
- `trust_label`: label after the trust count (default: `"users"`)
- `trust_message`: overrides the default “Trusted by …” line (supports HTML)
- `size`: avatar size class (default: `"size-10"`)
- `overlap`: avatar overlap class (default: `"-ml-3"`)
- `text_color`: custom primary text color (Tailwind class or CSS color like `#fff`)
- `secondary_text_color`: custom secondary text color (Tailwind class or CSS color like `#fff`)
- `avatar_border`: custom avatar border class (overrides theme default)
- `star_color`: custom star color (Tailwind class or CSS color like `#facc15`)
---
## Testimonials
The `testimonials` component showcases user testimonials (in text, video, or photo format) in a fully responsive and beautiful **masonry grid**.
The `testimonials` component can also be used as a **carousel** instead of a masonry grid, to highlight specific testimonials:
The component draws all testimonials from the config file `config/testimonials.yml`
Config structure:
- `default`
- `accent_color`: highlight/accent color shown in all testimonial cards (example: `"#df430d"`)
- `accent_text_color`: text color on highlighted/accented text (example: `"#fff"`)
- `star_max`: maximum number of stars on testimonial cards (default: `5`)
- `default_avatar_url`: fallback avatar when `avatar_url` is missing
- `development` / `test` / `production`: you can have different sets of configurations and testimonials per environment
- `testimonials`: array of testimonial entries
>[!TIP] To highlight any text in the testimonial, wrap any words in double brackets to highlight them with your accent color, like this: `Launched [[in a week]]`
Example config:
```yaml
default: &defaults
accent_color: "#df430d"
accent_text_color: "#fff"
star_max: 5
default_avatar_url: "https://example.com/avatar.jpg"
development:
<<: *defaults
testimonials:
- kind: text
text: "Launched [[in a week]] and seeing great user engagement."
name: "Charlie Quinn"
rating: 5
- kind: video
quote: "Everything you need to build your app fast"
name: "Morgan Casey"
rating: 5
video:
mp4_url: "https://example.com/video.mp4"
poster_url: "https://example.com/image.jpg"
```
Testimonial entry (common fields):
- `kind`: one of `text`, `image`, `text_image`, `video`, `tweet`
- `name`: person name
- `position`: job title (optional)
- `company`: company name (optional)
- `handle`: social handle (optional)
- `avatar_url`: image URL (optional; falls back to `default_avatar_url`)
- `rating`: number of stars to show (optional)
- `date`: date string (optional)
- `tags`: array of labels (optional; useful to curate subsets like `featured` or `demo`)
For testimonials of different `kind`s, you can add specific fields:
- `text`
- `text`: testimonial body
- `image`
- `image_url`: image URL to display
- `text_image`
- `text`: testimonial body
- `image_url`: image URL to display
- `video`
- `quote`: short highlighted quote
- `video.mp4_url`: MP4 URL
- `video.poster_url`: poster image URL (optional but recommended)
- `tweet`
- `text`: tweet body
- `source.label`: source name (example: `"X"`) (optional)
- `source.url`: source URL (optional)
---
## FAQ System
The Frequently Asked Questions section is arguably one of the highest-converting sections of any website, and even
more important than the question and the answers themselves is whether the questions can be linked to uniquely and
individually as "sub-pages".
The FAQ component in RailsFast does this: not only it allows any arbitrary content in your answers, but it also provide you with unique links to each question, to help LLMs and search engines understand your website and product better, and to help users share specific questions with one unique link.
It's almost as if with RailsFast you have FAQ questions automatically mounted as independent blog posts searchable by
Google.
The FAQ component renders a beautiful accordion, and auto-generates **unique, shareable links per question** (great for SEO, LLMs, and support).
Use it like:
```ruby
<%= render "components/railsfast/marketing/faq",
title: "Frequently Asked Questions",
subtitle: "Everything you need to know before getting started.",
theme: "light" %>
```
The component draws all questions from `config/faqs.yml` (via `Rails.configuration.x.faq[:faqs]`).
Linking / stable URLs:
- Each question gets an `id="faq-..."` so it can be linked as `#faq-your-question-slug`.
- If you rely on stable links, **set an explicit `id` per FAQ item** so you can safely edit the question text later.
Example config:
```yaml
development:
faqs:
- id: pricing
question: "Do you have a free trial?"
answer: |
Yes — you can try it for 14 days.
```
>[!IMPORTANT] If you don’t set `id`, the anchor is derived from the question text. Since each question gets its unique link based on the question text, **do not** change the question text or it'll break the link.
Parameters:
- `items`: array of FAQ items (optional; overrides config)
- `ids`: only include specific FAQ IDs (optional)
- `limit`: max number of items to show (optional)
- `title`: section heading (optional)
- `subtitle`: section subheading (optional)
- `section_id`: HTML id for the whole section (default: `"faq"`)
- `multiple_open`: allow multiple items to be open at once (default: `true`)
- `padding`: section padding classes (default: `"py-12 sm:py-16"`)
- `theme`: `light` (default) or `dark`
Style overrides (override theme defaults):
- `item_bg`, `item_border`, `item_shadow`, `item_rounded`, `item_hover`
- `question_text`, `answer_text`, `icon_color`
- `item_gap`
---
## Features
Use the `features` component to showcase features of your product.
You can use it in a handful of `mode`s; in all of them you pass an `items` array and it renders a beautiful features section.
Use it like:
```ruby
<%= render "components/railsfast/marketing/features",
mode: :grid,
title: "Everything you need",
subtitle: "A fast overview of what you get out of the box.",
title_alignment: "center",
padding: "py-16 sm:py-24",
items: [
{ title: "Fast setup", description: "Ship in days, not weeks.", icon: "bolt" },
{ title: "Secure by default", description: "Sane defaults everywhere.", icon: "shield-check" }
] %>
```
Common parameters (wrapper):
- `mode`: one of the modes below (default: `:grid`)
- `items`: array of feature items (required)
- `title`: section title (optional)
- `subtitle`: section subtitle (optional)
- `title_alignment`: `"center"` (default), `"left"`, `"right"`
- `title_max_width`: `"2xl"` (default), `"3xl"`, `"4xl"`, `"full"`
- `padding`: section padding classes (default: `"py-16 sm:py-24"`)
Icons:
- For the modes that support it, `icon` uses [Heroicons](https://heroicons.com) names (example: `"bolt"`, `"shield-check"`).
## Feature modes
### Grid mode
`:grid`: card grid with optional badges, bullet points, and CTA. Good to showcase multiple features at once
Item fields: `{ title:, description:, icon:, badge:, points:, cta: }`
### Alternating mode
`:alternating`: the bread and butter of features in landing pages. Rows that alternate their alignment left and right, with big images on the other side. Good for telling a story
Item fields: `{ title:, description:, image:, image_class:, points: }`
Specific paramenters:
- `image_aspect`: `"video"` (default), `"square"`, `"photo"`
- `layout`: `"balanced"` (default), `"image_large"`, `"content_large"`
- `compact`: `true`/`false`
### Bento mode
`:bento`: “bento box” mosaic for mixed-size, more visual feature blocks
Item fields: `{ title:, description:, bento_size:, image:, image_position:, icon:, badge:, points:, cta:, extra_content: }`
### Icons mode
`:icons`: best for a section of 3 highlighted features
Item fields: `{ icon:, title:, description:, cta:, accent_color: }`
Specific parameters:
- `columns`: `2`, `3` (default), `4`, `6`
- `icon_style`: `"rounded"` (default), `"circle"`, `"square"`, `"none"`
- `icon_size`: `"small"`, `"medium"` (default), `"large"`
- `icon_variant`: `:solid` (default) or `:outline`
- `accent_color`: hex/CSS color (example: `"#84cc16"`) — can also be set per item via `accent_color`
- `alignment`: `"center"` (default) or `"left"`
- `compact`: `true`/`false`
### List mode
`:list`: simple checkmark list in different columns that looks clean and organized
Item fields: either a string (`"Simple feature"`) or `{ title:, description: }`
---
## Call to action
## Call To Action (CTA)
The `cta` component is a prominent “call to action” section (usually near the bottom of a landing page) to convert readers into signups, demo views, or purchases.
>[!TIP] This component intentionally has **no background** and doesn’t hardcode text colors — style the parent container and it will inherit.
Use it like:
```ruby
<%= render "components/railsfast/marketing/cta",
title: "Ready to get started?",
subtitle: "This is the perfect spot for your first major call-to-action (CTA). Users have just finished reading about your product's main benefits and are primed to take the next step. Use this CTA to convert interest into action: invite them to get started or explore a live demo.",
primary_button: {
text: "Get started now",
url: new_user_registration_path,
style: "solid",
size: "medium"
},
secondary_button: {
text: "View Demo",
url: "#",
style: "outline",
show_arrow: true
},
layout: "centered",
icon: "rocket-launch",
stats: [
{ value: "10K+", label: "Customers" },
{ value: "+40%", label: "Revenue" },
{ value: "99.9%", label: "Uptime" }
]
%>
```
Parameters:
- `title`: main heading text (default: `"Ready to get started?"`)
- `subtitle`: supporting text (optional)
- `layout`: `"centered"` (default), `"split"`, `"offset"`
- `container`: `"normal"` (default), `"wide"`, `"full"`
- `icon`: optional Heroicon name (example: `"rocket-launch"`)
- `stats`: optional array of `{ value:, label: }`
- `primary_button`: hash with:
- `text`, `url`
- `style`: `"solid"` (default), `"outline"`, `"ghost"`
- `size`: `"small"`, `"medium"`, `"large"` (default), `"xlarge"`
- `icon`: optional Heroicon name
- `color`, `text_color`: optional hex/CSS colors (example: `"#df430d"`)
- `data`: optional Rails `data:` hash
- `secondary_button` (optional): hash with:
- `text`, `url`
- `style`: `"outline"` (default), `"solid"`, `"ghost"`
- `icon`: optional Heroicon name
- `show_arrow`: `true`/`false`
- `data`: optional Rails `data:` hash
## Call To Action Card (CTA Card)
The `cta_card` component is an attention grabber: a single CTA inside a card, with optional gradients, patterns, and a featured image.
Use it like:
```ruby
<%= render "components/railsfast/marketing/cta_card",
title: "Another catchy CTA in a card",
subtitle: "Use eye-catching gradients to grab users' attention and drive them to your CTA.",
button: {
text: "Start now",
url: new_user_registration_path,
style: "solid",
icon: "arrow-right",
color: "white",
data: { turbo: false }
},
image: {
url: "https://placehold.co/600x400/e4e4e4/c4c4c4?text=Your+featured+image+here",
alt: "RailsFast dashboard screenshot",
position: "right"
},
background: "gradient",
gradient_colors: ["#00b09b", "#96c93d"],
image_effects: "tilted" %>
```
Parameters:
- `title`: main heading text
- `subtitle`: supporting text
- `button`: hash with `text`, `url`, `style` (`"solid"`, `"outline"`, `"ghost"`), optional `icon`, optional `color`/`text_color`, optional `data`
- `image` (optional): hash with `url`, `alt`, `position` (`"right"`, `"left"`, `"background"`)
- `background`: `"gradient"` (default), `"solid"`, `"pattern"`
- `gradient_colors`: array of 2–3 colors (Tailwind names or hex/CSS colors)
- `background_color`: base color for `"solid"` / `"pattern"`
- `decorative_blobs`: `true`/`false` (default: `true`)
- `card_style`: `"elevated"` (default), `"bordered"`, `"minimal"`
- `image_effects`: `"tilted"` (default), `"shadow"`, `"glow"`, `"none"`
---
## Pricing page
RailsFast also provides a fully working pricing page with pricing cards, a toggle to choose between monthly / yearly billing, and more.
As described in the [[features/0-payments|Payments]] section, this pricing page is already fully integrated with the [`pricing_plans`](https://github.com/rameerez/pricing_plans) gem, and it's not just front-end / aesthetics: all plans can be purchased and whatever is defined in the plan itself is immediately enforceable app-wide.
The pricing page uses the plans defined in `pricing_plans` if you’ve configured the gem, otherwise it falls back to the placeholder `@static_plans` in the `DemoController`, so the page still looks good.
What you’ll likely customize:
- **Your actual plans**: define them in `config/initializers/pricing_plans.rb` and plug in your Stripe Price IDs in `config/railsfast/railsfast.yml` as described in [[features/0-payments|Payments]].
- **Which plan is “Most Popular”**: mark a plan as highlighted in `pricing_plans.rb` (RailsFast auto-detects it), or pass a `popular_plan_id:` manually.
- **Monthly/Yearly toggle**: `show_toggle: true/false`
- **Scalable / enterprise slider card**: `show_scalable: true/false` + `scalable_plans: [...]`
- **Brand theming**: pass `accent:` (hex/CSS color string)
---
## Other components
## Navbar
It's a fully responsive navbar that works on desktop as well as in mobile, with a collapsible menu.
Use it like:
```ruby
<%= render 'layouts/partials/navbar', variant: 'floating' %>
```
`variant` can be:
- `sticky`
- `regular`
- `floating`
## Footer
## Other components
RailsFast uses the `tailwindcss-stimulus-components` library, so all these other components are also available and ready to use:
- `Alert`
- `Autosave`
- `Color Preview`
- `Dropdown`
- `Modal`
- `Popover`
- `Slideover`
- `Tabs`
- `Toggle`
To learn how to use these components, read the official [`tailwindcss-stimulus-components`](https://github.com/excid3/tailwindcss-stimulus-components).
---
## Customizing components
RailsFast components are meant to be edited. The defaults are great, but your product should look like **your** product.
If you want to customize a component provided by RailsFast, here’s the simplest workflow:
- **Copy the component out of the RailsFast folder before editing**: take the partial from `app/views/components/railsfast/...` and copy it into `app/views/components/...`
- **Edit your component**: restyle, change layout, edit copy, whatever
- **Keep upstream updates clean**: since your customized component lives outside `components/railsfast`, you can keep pulling template updates without getting messy conflicts in your edited files.
>[!TIP] One useful idea is to ask an AI to refactor the Tailwind classes of a component you'd like restyled. It works best if you also paste a “reference” of what you like (a picture, or HTML code), so the AI can get the vibe better
If you'd rather override the RailsFast components themselves in place, you'd need to add it to the `.gitattributes` file with `merge=ours`, like this:
```gitattributes
app/views/components/marketing/_hero.html.erb merge=ours
```
So that when I update the RailsFast template and you do fetch the changes, you don't get your custom component overriden by the upstream template changes.
---
## AI image generation
---
title: AI image generation
sort: 2
---
---
## LLMs and AI chats
---
title: LLMs and AI chats
sort: 3
---
---
## AI, vector databases, and RAG
RailsFast includes built-in support for AI-powered features, vector search, and Retrieval-Augmented Generation (RAG) to help you build intelligent applications.
## Overview
Modern Rails applications can leverage AI for:
- Semantic search and similarity matching
- Content recommendations
- Document question-answering
- Intelligent data retrieval
- Contextual assistance
## Vector Databases
Vector databases store embeddings (numerical representations of text) that enable semantic search.
### Supported Solutions
RailsFast works with:
- **pgvector** - PostgreSQL extension (recommended for most projects)
- **Pinecone** - Managed vector database
- **Weaviate** - Open-source vector search engine
- **Milvus** - Scalable vector database
### Using pgvector
pgvector is included in your PostgreSQL setup.
#### Enable the Extension
```ruby
# db/migrate/xxx_enable_pgvector.rb
class EnablePgvector < ActiveRecord::Migration[8.0]
def change
enable_extension 'vector'
end
end
```
#### Create a Model with Embeddings
```ruby
# db/migrate/xxx_add_embeddings_to_documents.rb
class AddEmbeddingsToDocuments < ActiveRecord::Migration[8.0]
def change
add_column :documents, :embedding, :vector, limit: 1536
add_index :documents, :embedding, using: :ivfflat, opclass: :vector_cosine_ops
end
end
```
```ruby
# app/models/document.rb
class Document < ApplicationRecord
has_neighbors :embedding
end
```
## Generating Embeddings
### Using OpenAI
Install the OpenAI gem:
```ruby
# Gemfile
gem 'ruby-openai'
```
Generate embeddings:
```ruby
# app/services/embedding_service.rb
class EmbeddingService
def self.generate(text)
client = OpenAI::Client.new(
access_token: Rails.application.credentials.dig(:openai, :api_key)
)
response = client.embeddings(
parameters: {
model: "text-embedding-3-small",
input: text
}
)
response.dig("data", 0, "embedding")
end
end
```
Use it in your model:
```ruby
class Document < ApplicationRecord
has_neighbors :embedding
after_save :generate_embedding, if: -> { content_changed? }
private
def generate_embedding
update_column :embedding, EmbeddingService.generate(content)
end
end
```
## Semantic Search
### Finding Similar Documents
```ruby
# Find documents similar to a query
query = "How do I set up payments?"
query_embedding = EmbeddingService.generate(query)
similar_docs = Document.nearest_neighbors(
:embedding,
query_embedding,
distance: "cosine"
).limit(5)
```
### Building a Search Controller
```ruby
# app/controllers/search_controller.rb
class SearchController < ApplicationController
def show
@query = params[:q]
return if @query.blank?
query_embedding = EmbeddingService.generate(@query)
@results = Document.nearest_neighbors(
:embedding,
query_embedding,
distance: "cosine"
).limit(10)
end
end
```
## RAG (Retrieval-Augmented Generation)
RAG combines vector search with LLMs to provide contextual answers.
### Basic RAG Implementation
```ruby
# app/services/rag_service.rb
class RagService
def self.answer_question(question)
# 1. Find relevant documents
query_embedding = EmbeddingService.generate(question)
relevant_docs = Document.nearest_neighbors(
:embedding,
query_embedding,
distance: "cosine"
).limit(3)
# 2. Build context from documents
context = relevant_docs.map(&:content).join("\n\n")
# 3. Generate answer with LLM
client = OpenAI::Client.new(
access_token: Rails.application.credentials.dig(:openai, :api_key)
)
response = client.chat(
parameters: {
model: "gpt-4-turbo-preview",
messages: [
{
role: "system",
content: "Answer questions based on the provided context."
},
{
role: "user",
content: "Context:\n#{context}\n\nQuestion: #{question}"
}
]
}
)
response.dig("choices", 0, "message", "content")
end
end
```
### RAG Controller
```ruby
# app/controllers/ai_assistant_controller.rb
class AiAssistantController < ApplicationController
def ask
@question = params[:question]
@answer = RagService.answer_question(@question)
respond_to do |format|
format.json { render json: { answer: @answer } }
format.html
end
end
end
```
## Advanced Patterns
### Hybrid Search
Combine traditional search with vector search:
```ruby
class Document < ApplicationRecord
has_neighbors :embedding
def self.hybrid_search(query, limit: 10)
# Vector search
query_embedding = EmbeddingService.generate(query)
vector_results = nearest_neighbors(
:embedding,
query_embedding,
distance: "cosine"
).limit(limit * 2)
# Keyword search
keyword_results = where("content ILIKE ?", "%#{query}%")
.limit(limit * 2)
# Combine and deduplicate
(vector_results + keyword_results).uniq.take(limit)
end
end
```
### Caching Embeddings
Cache embeddings to reduce API costs:
```ruby
class EmbeddingService
def self.generate(text)
cache_key = "embedding:#{Digest::SHA256.hexdigest(text)}"
Rails.cache.fetch(cache_key, expires_in: 30.days) do
# API call to generate embedding
client = OpenAI::Client.new(...)
# ... generate and return embedding
end
end
end
```
### Batch Processing
Generate embeddings in background jobs:
```ruby
class GenerateEmbeddingsJob < ApplicationJob
queue_as :default
def perform(document_ids)
documents = Document.where(id: document_ids, embedding: nil)
documents.each do |doc|
doc.update(embedding: EmbeddingService.generate(doc.content))
end
end
end
# Usage
GenerateEmbeddingsJob.perform_later(Document.pluck(:id))
```
## Configuration
Add your AI service credentials:
```bash
EDITOR="cursor --wait" bin/rails credentials:edit
```
```yaml
openai:
api_key: sk-...
# Or for other services
anthropic:
api_key: sk-ant-...
pinecone:
api_key: ...
environment: us-east-1-aws
```
## Performance Tips
1. **Index your embeddings**: Use appropriate index types (IVFFlat, HNSW)
2. **Batch embed operations**: Generate embeddings in batches for better throughput
3. **Cache frequently used embeddings**: Reduce API costs
4. **Use smaller models when possible**: `text-embedding-3-small` is faster and cheaper
5. **Implement retry logic**: Handle API failures gracefully
## Cost Optimization
- Cache embeddings aggressively
- Use background jobs for non-urgent embedding generation
- Choose appropriate embedding models (smaller = cheaper)
- Implement rate limiting on user queries
- Monitor usage and set budgets
## Example Use Cases
### Documentation Search
```ruby
# Find relevant docs for a user question
question = "How do I configure email?"
answer = RagService.answer_question(question)
```
### Content Recommendations
```ruby
# Find similar articles
article = Article.find(params[:id])
similar = Article.nearest_neighbors(:embedding, article.embedding).limit(5)
```
### Intelligent Support Bot
```ruby
# Answer customer questions
customer_query = "Do you offer refunds?"
support_answer = RagService.answer_question(customer_query)
```
## Resources
- [pgvector GitHub](https://github.com/pgvector/pgvector)
- [Neighbor gem (Rails integration)](https://github.com/ankane/neighbor)
- [OpenAI Embeddings Guide](https://platform.openai.com/docs/guides/embeddings)
- [RAG Explained](https://www.anthropic.com/research/rag)
## See Also
- [[background-jobs|Background Jobs]] - For async embedding generation
- [[configuration|Configuration]] - Setting up AI credentials
- [[development|Development]] - Testing AI features locally
---
## How to get a 10/10 deliverability score for your emails
RailsFast can achieve a 10/10 email deliverability out of the box, as tested by mail-tester.com
To achieve that, when configuring AWS SES for sending email, make sure to strictly follow these steps:
- Navigate to your Amazon SES dashboard
- Add your project domain name as a SES identity
- To do that, on the sidebar, navigate to Configuration > Identities and click [ Create Identity ]
- In the new page, select "Domain" as the new identity type
- Enter your domain
- Activate the "Use a custom MAIL FROM domain" checkbox
- In the MAIL FROM field, input something like `mail.`
- Scroll down and select "Easy DKIM"
- Select RSA_2048_BIT
- Leave "DKIM Signatures" active
- Click on the [ Crete identity ] button
- On the new identiy page, you'll see three main sections: DKIM, MAIL FROM, and DMARC. For each section, there's a "Publish DNS records" unfoldable item. If you click it, you'll see the DNS records you need to add to your domain's DNS to clear each section. It's a somewhat tedious job, but make sure you add all records right.
- Once you're done adding all records, scroll up in the indentity page in SES and click the refresh button. If everything went right, you should see your "Identity status: Verified", and the DKIM and MAIL FROM sections marked as successful. If that did not happen, give your DNS time to propagate (anywhere from some minutes to a few hours) and try again.
- RailsFast is configured with best email practices so that when you send a transactional email from Rails you should get a perfect 10/10 score (or pretty close to it) in deliverability: you can test it at mail-tester.com
---
## Kamal - Configure a container registry
Kamal uses your local development machine as a Docker container registry by default.
This means that, after the Docker image containing your app is built, it's stored in your local computer and needs to be uploaded from your computer to the production server for deployment.
If your internet connection is not the best, this may slow down your total deployment time from the time you hit `kamal deploy` until the time everything is live in production. Also, you may be filling up your local machine with big stale Docker images.
> [!TIP]
> Using a remote container registry usually speeds up deployment times.
There are many remote container registry options: Docker Hub, ghcr.io, GitLab container registry, etc.
Some are paid, some are free (but always public, which is not supported on RailsFast).
What I found to be easiest to configure and integrate with Kamal (RailsFast) is AWS ECR.
## How to use AWS ECR with Kamal as container registry
AWS ECR costs close to nothing (I paid $0.32 –yes, 32 cents– last month), and it's very straightforward to integrate with Kamal using the AWS CLI. And since we're already using AWS for storage (S3) and emailing (SES) in RailsFast, it makes sense to also leverage their container registry.
### Install and configure the AWS CLI
First, [install and configure the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html).
Then, at the end of the Kamal secrets file under `.kamal/secrets`, uncomment this line:
```
AWS_ECR_PASSWORD=$(aws ecr get-login-password --region us-east-1 --profile default)
```
Make sure to change the `us-east-1` for the region you're using in ECR, or leave it like that if you're fine with us-east-1.
> [!INFO]
> This is a secure way of sharing your AWS ECR credentials with Kamal for deployment. No password, API token or anything sensible ever gets commited or made public. Upon every deployment, Kamal will call the AWS CLI on your local computer to get a temporary login password for ECR, and will load that token as the `AWS_ECR_PASSWORD` ENV var in the containers that need it.
### Create a private registry on AWS ECR
Go to your AWS console and navigate to [ECR (Elastic Container Registry)](https://us-east-1.console.aws.amazon.com/ecr/private-registry/repositories?region=us-east-1).
Click on the ` [ Create repository ] ` button.
Give your registry a name with a username in front. Example: use `myusername/myproject`, not just `myproject`. This is because Docker container registries require username namespacing (a container must belong to a username). Tip: just use the same username you use on GitHub and the same project name you use on GitHub.
Keep the rest of the settings default and click on `[ Create ]`
Since we're at it, let's do a couple more clicks to limit how many versions of our Docker images the registry is going to store (saving us storage money!)
Select your recently created repository from the list of private repositories. On top, click `[Actions ▼]` and then `Lifecycle policies`. Click on `[Create rule]` to create a new rule, and in the new screen leave everything by default but where it says: `Image tag status` (select: `Any`). Then, under `Match criteria`, I suggest you select `Days since image created` or `Image count`. In the box below, you can specify for how many days you want to store each version, or how many version (images) in total you want to storage.
I personally just select `Image count`, and set it to only save the last `30` images. Click `[Save]` and `[Confirm]` and you won't be storing old, stale versions forever.
### Edit `deploy.yml`
Now that we have everything configured in ECR, in your `config/deploy.yml` let Kamal know which container registry you're using:
- Change your `image` name to match the name you gave it in AWS ECR. Example, if before you had something like:
```yaml
image: myproject
```
Edit it to make it match what you had on ECR:
```yaml
image: myusername/myproject
```
- Remove the `localhost:5555` `server` under the `registry` section and uncomment the placeholder remote container registry options (`server`, `username`, `password`), to end up with something like this:
```yaml
registry:
server: 1234.dkr.ecr.us-east-1.amazonaws.com
username: AWS
password:
- AWS_ECR_PASSWORD
```
Replace the placeholder `1234.dkr.ecr.us-east-1.amazonaws.com` with the actual repository URI you can see on ECR in your private repositories list.
> [!IMPORTANT]
> If you're using a remote builder, make sure to pass the `AWS_ECR_PASSWORD` ENV var to the builder too, under the `secrets` section of the `builder` key:
>
> ```
> builder:
> secrets:
> - RAILS_MASTER_KEY
> - AWS_ECR_PASSWORD
>```
And you're done!
You can check the remote Docker container registry in AWS ECR is set up correctly in Kamal by running:
```bash
kamal registry setup
```
And seeing that it logs in correctly.
The next time you deploy using `kamal deploy`, Kamal will push and pull the Docker images from ECR instead of from your local computer registry.
---
You can learn more about Kamal and container registries in the [official Kamal docs](https://kamal-deploy.org/docs/configuration/docker-registry/)
---
## Kamal - Configure a remote builder
> [!IMPORTANT]
> If you're on macOS, especially M1, M2, M3... (Apple Silicon), you may **have to** use a remote builder or your build process may fail.
A remote builder is just a server that builds your app for you, instead of doing the build work in your local machine. You send your code to the remote builder, and it produces the deployeable Docker image.
This is useful, for example, in case your local development machine is too slow building, and you want to speed things up.
It may also me something you **need to** to if you're on macOS.
The newest macOS machines (the Apple Silicon family: M1, M2, M3, etc.) may find issues with Docker (the underlying technology Kamal uses for building and deploying your app); **this is fully solvable** if you use a remote builder instead of your local Mac for building.
## How to set up a remote builder server in Kamal
Setting up a remote builder couldn't be easier: just spin up a new server on Hetzner exactly as you did on the [[getting-started/2-quickstart|Quickstart]]. In a nutshell: select a server and a location, add your SSH key, __no need__ for backups this time, and in the **Cloud Config** box, paste this:
```yaml
#cloud-config
runcmd:
- bash -lc 'set -euo pipefail; wget -qO /root/railsfast-setup.sh https://setup.railsfast.com; chmod +x /root/railsfast-setup.sh; /root/railsfast-setup.sh 2>&1 | tee -a /var/log/railsfast-setup.log && reboot'
```
Create the server and wait for the setup script to finish as described in the [[getting-started/2-quickstart|Quickstart]].
Then, in your `deploy.yml` file, uncomment the remote builder machine section under the `builder` key:
```yaml
builder:
arch: amd64
# Uncomment this:
local: false
remote: ssh://docker@5.6.7.8
args:
RUBY_VERSION: 3.4.7
secrets:
- RAILS_MASTER_KEY
```
And change the placeholder `5.6.7.8` IP for the actual IP of the server you've just created.
That's it! Try deploying with `kamal deploy` now and your setup will use the remote builder for building, instead of your local machine.
---
## Migrating Existing Project
Here's the simplest, most reliable way to "adopt" RailsFast into an existing Rails project and start getting updates.
### Goal
- Link your project’s history to the RailsFast template so future updates are just normal git merges.
- Keep your current code as-is during adoption.
- Preserve secrets/config with merge guards.
### One-time adoption (manual)
1) Prep
- Commit or stash everything: `git status` should be clean.
- Create a safety branch: `git checkout -b adopt-railsfast-backup`
2) Add RailsFast as a remote
```bash
git remote add railsfast git@github.com:railsfast/railsfast-base.git
git fetch railsfast --prune
```
3) Establish a baseline relationship (don’t change your files)
- This records the template as a merge parent without altering your tree.
```bash
git merge --allow-unrelated-histories -s ours --no-ff -m "Adopt RailsFast upstream at baseline" railsfast/main
```
- Result: histories are now related. Future `git merge railsfast/main` will bring only changes made in the template since this baseline.
4) Add merge guards so protected files always keep your local version
- If you already have `.gitattributes`, add lines like:
```bash
echo '# RailsFast: Preserve local configuration during template merges' >> .gitattributes
echo 'Dockerfile merge=ours' >> .gitattributes
echo '.kamal/secrets merge=ours' >> .gitattributes
echo 'app/views/pwa/manifest.json.erb merge=ours' >> .gitattributes
echo 'config/application.rb merge=ours' >> .gitattributes
echo 'config/database.yml merge=ours' >> .gitattributes
echo 'config/deploy.yml merge=ours' >> .gitattributes
echo 'config/railsfast.yml merge=ours' >> .gitattributes
echo 'config/credentials.yml.enc merge=ours' >> .gitattributes
echo 'config/master.key merge=ours' >> .gitattributes
echo 'config/db.key merge=ours' >> .gitattributes
```
- Configure the merge driver + rerere:
```bash
git config merge.ours.driver true
git config rerere.enabled true
git add .gitattributes
git commit -m "Configure RailsFast merge guards"
```
5) (Optional) Record metadata for RailsFast CLI status
- If your repo already includes `bin/railsfast`, you can record the baseline and remote without changing your app:
```bash
bin/railsfast init --yes --skip-rename --skip-setup
```
- This writes `.railsfast/config.json` and remembers the remote/branch/baseline. If you don’t have the scripts yet, you can skip this step.
### Verifications
- Check relation and baseline:
```bash
git log --oneline --graph --decorate --all | head -n 50
```
- See behind count:
```bash
git rev-list --count HEAD..railsfast/main
```
---
## Deploy multiple Rails apps to the same server
Running multiple apps in the same VPS server is trivial with RailsFast.
Just configure `deploy.yml` to deploy to the same server IP, that's it! Nothing else to do on your side.
All apps in the same server will have separate databases and run in separate containers so there's no risk of cross-pollination whatsoever. Your RailsFast apps are like silos, they just happen to be in the same VPS!
---
## Updating RailsFast
Updating any template is always going to be a bit difficult, because as you start building your app on top of it many files will diverge from the original template, and conflicts will naturally arise. The worst-case scenario is you need to go file-by-file reviewing the template updates and deciding what to incorporate into your app.
HOWEVER, RailsFast is designed so the amount of effort you need to do is minimized, and ideally you can keep pulling RailsFast improvements into your app without much work.
## Make your life easier for updating
As we covered in the [[ui-components/9-customizing|Customizing Components]] section, the best practice if you plan to customize RailsFast components, is that you **don’t edit the RailsFast components in-place**, instead, copy the component you want to change into your own folder and edit your copy.
## Protect your customizations (merge guards)
At some point, you will inevitably edit RailsFast core files. If you *did* edit files that ship with RailsFast and that you can't copy easily into your own folder (for example, layouts or auth views), you can tell Git to always keep *your* version on merges by adding merge guards in `.gitattributes`.
Example:
```gitattributes
# Keep my customized files when merging template updates
app/views/devise/** merge=ours
```
This way, if for example you update all your authentication screens to match your branding and design, when updating RailsFast your files will get priority, overriding the update, and you won't lose your work (you won't get updates on those files, though)
>[!WARNING] My recommendation is you use this only for individual files or small folders. If you guard huge paths like `app/views/**`, you will also silently skip upstream improvements (including fixes) for those files.
## How to update RailsFast
Since we configured the remote in the quickstart, we can just `pull` the latest RailsFast changes:
```bash
git fetch railsfast --prune
```
And then merge them with your project:
```bash
git merge railsfast/main
```
If you get conflicts, resolve them (Git will respect your `.gitattributes` merge guards), and then commit the merge.
## After updating
Run any new migrations:
```bash
bin/rails db:migrate
```
Restart your dev server (`bin/dev`) and make sure nothing got broken in the update and everything works.
---