A modern Rails 8.1 + React SaaS starter with authentication, admin panel, and Docker deployment.
- Backend: Rails 8.1, PostgreSQL, Puma
- Frontend: React 19, TypeScript, Vite, Tailwind CSS 4
- Integration: Inertia.js (seamless frontend-backend)
- Auth: Passwordless magic link + Google OAuth
- Admin: Avo admin panel
- I18n: Rails i18n + i18n-js (URL-based locale switching)
- Deployment: Kamal (Docker-based)
- Ruby 3.4+
- Node.js 22+
- PostgreSQL
# Clone and install dependencies
git clone https://github.com/goodmatedesign/rails_inertia_starter
cd rails_inertia_starter
bin/setupbin/dev # Starts Rails + ViteVisit http://localhost:3000
Edit credentials with:
bin/rails credentials:editAdd these keys:
# Google OAuth (required for Google sign-in)
google:
client_id: your_google_client_id
client_secret: your_google_client_secret- Go to Google Cloud Console
- Create a new project or select existing
- Go to "Credentials" > "Create Credentials" > "OAuth client ID"
- Choose "Web application"
- Add authorized redirect URIs:
- Development:
http://localhost:3000/auth/google_oauth2/callback - Production:
https://yourdomain.com/auth/google_oauth2/callback
- Development:
- Copy Client ID and Client Secret to Rails credentials
Development uses PostgreSQL with default settings. Configure in config/database.yml if needed.
bin/rails db:prepare # Create and migrate
bin/rails db:reset # Reset databaseUsers enter email, receive a 6-digit verification code valid for 15 minutes.
Rate Limiting:
- Email submission: 10 attempts per 3 minutes
- Code verification: 10 attempts per 15 minutes
"Continue with Google" button on sign-in page.
If a user signs up with email first, then uses Google (same email), accounts are automatically linked.
Make a user admin via Rails console:
bin/rails console
> User.find_by(email: "[email protected]").update!(admin: true)Admin panel available at /avo (admin users only).
For production, create separate credentials:
EDITOR="code --wait" bin/rails credentials:edit --environment productionAdd the same keys as development credentials.
Additionally, create config/postgres.key with your production database password.
Check .kamal/secrets for details:
RAILS_MASTER_KEY=$(cat config/credentials/production.key)
POSTGRES_PASSWORD=$(cat config/postgres.key)-
Update
config/deploy.yml:- Set your server IP
- Set your domain
- Configure registry settings ( we use local registry by default, so you can skip this step )
-
Deploy:
bin/kamal setup # First deploy
bin/kamal accessory boot db # Start database
bin/kamal deploy # Subsequent deploysERROR (SSHKit::Command::Failed): Exception while executing on host 12.222.123.123: docker exit status: 1
docker stdout: Nothing written
docker stderr: permission denied while trying to connect to the docker API at unix:///var/run/docker.sock
Check https://kamal-deploy.org/docs/configuration/ssh/
ERROR (SSHKit::Command::Failed): Exception while executing on host 12.222.123.123: docker exit status: 1
docker stdout: Nothing written
docker stderr: Error: target failed to become healthy within configured timeout (30s)
This probably means that your server DB is not reachable from the app container. Check your config/deploy.yml and make sure that the DB configuration is correct. and you have started the DB accessory with bin/kamal accessory boot db
This app supports multiple languages using Rails i18n on the backend and i18n-js on the frontend.
- English (
en) - default - Chinese (
zh)
- URL-based: Locale is determined by URL prefix (
/en/...,/zh/...) - Session persistence: Locale is stored in session for non-localized routes (e.g., OAuth callbacks)
- Shared translations: Rails YAML translations are exported to JSON for React
- Add translations to
config/locales/en.ymlandconfig/locales/zh.yml:
# config/locales/en.yml
en:
my_feature:
title: "My Feature"
description: "Feature description"- Export translations to frontend:
bundle exec i18n export- Use in React components:
import { useI18n } from "@/hooks/use-i18n";
export default function MyComponent() {
const { t } = useI18n();
return <h1>{t("my_feature.title")}</h1>;
}- Use in Rails controllers/views:
flash[:notice] = t("flash.signed_in")- Add locale to
config/application.rb:
config.i18n.available_locales = [:en, :zh, :es] # Add :es-
Create locale file
config/locales/es.yml -
Update
app/frontend/types/index.ts:
export type Locale = "en" | "zh" | "es";- Update
app/frontend/components/locale-switcher.tsx:
const localeNames: Record<string, string> = {
en: "EN",
zh: "中文",
es: "ES",
};- Import new locale in
app/frontend/lib/i18n.ts:
import es from "@/locales/es.json";
const i18n = new I18n({
...en,
...zh,
...es,
});- Export translations:
bundle exec i18n exportAdd to config/initializers/i18n.rb:
Rails.application.config.after_initialize do
require "i18n-js/listen"
I18nJS.listen
endThis auto-exports translations when locale files change.
Always include the locale in links:
import { usePage } from '@inertiajs/react'
import type { SharedProps } from '@/types'
const { locale } = usePage<SharedProps>().props
<Link href={`/${locale}/posts`}>Posts</Link>Dark mode is supported via a custom useAppearance hook with Light/Dark/System modes. The app uses CSS variables with OKLch color space for modern color handling.
- Theme preference is persisted in localStorage
- System mode follows OS preference and updates automatically
- Theme is initialized before React hydration to prevent flash
Toast notifications are provided by sonner with automatic dark mode support.
bin/dev supports multiple process managers (in order of preference):
- overmind
- hivemind
- foreman
The CI suite (bin/ci) runs:
- RuboCop (linting)
- Brakeman (security analysis)
- bundler-audit (gem vulnerability scanning)
- Rails tests
- System tests
bin/rails test # Run all tests
bin/rails test:system # System tests
npm run check # TypeScript check
bin/rubocop # Ruby linting
bin/brakeman # Security scan
bin/ci # Full CI suiteapp/
├── controllers/ # Rails controllers
├── frontend/ # React frontend
│ ├── components/ # Shared components
│ ├── pages/ # Inertia pages
│ ├── hooks/ # React hooks
│ ├── locales/ # Exported JSON translations
│ ├── lib/ # Utilities (i18n setup, etc.)
│ └── types/ # TypeScript types
├── models/ # Rails models
├── mailers/ # Email templates
└── avo/ # Admin panel resources
config/
├── locales/ # Rails YAML translations (en.yml, zh.yml)
└── i18n.yml # i18n-js export configuration
MIT