A rails new template for building production Rails apps that are meant to
be worked on by AI coding agents (Claude Code and friends) as much as by
humans. It's the template ZAR uses internally — the same one our
non-programmer CEO uses to ship real apps to production — with the internal
plumbing stripped out so you can use it too.
"Agent-native" means the generated app doesn't just contain code — it contains
the operating manual an agent needs to change that code correctly: a
CLAUDE.md orientation file, 32 curated Claude skills, and 17 path-scoped
coding rules that auto-apply based on which file the agent is editing.
gh repo clone obie/rails-template ~/rails-template
MACHINA_PORT=3042 rails new myapp \
-m ~/rails-template/template.rb \
--skip-test --skip-gitMost Rails templates give you a stack. This one also gives you the conventions and guardrails that keep an autonomous agent on the rails (pun intended) while it builds features for you:
CLAUDE.md— a per-app orientation file: stack, auth flow, dev commands, conventions, and hard "never do this" rules an agent reads on the way in..claude/skills/— 32 curated skills covering Ruby/Rails craft (ruby,layered-rails,sandi-metz-rules,37signals-style,test-driven-development), a full Hotwire pack (hwc-*), workflow skills (writing-plans,executing-plans,using-git-worktrees,handoff), and app patterns (rails-activity-timeline,rails-tiptap-autosave,mcp-oauth-setup,design-system)..claude/rules/— 17 path-scoped rules (migrations, controller scoping, CRUD routing, service objects, testing conventions, schema hygiene, …). Each has apaths:frontmatter so the agent only loads the rules relevant to the file it's touching.
The result: you (or your CEO) describe a feature, and the agent builds it the way a senior Rails engineer on the team would — because the team's standards ship inside the repo.
- Rails edge (
github: "rails/rails", branch: "main"), Ruby 4.0.1 - SQLite in development/test, PostgreSQL in production
- Solid Queue / Cache / Cable + Mission Control (no Redis needed)
- Hotwire (Turbo + Stimulus), Tailwind, Importmap, Propshaft
- LLM stack: Raix + OpenRouter + RubyLLM (talk to Claude / OpenAI / Gemini)
- ViewComponent, Servus (service objects), state_machines, Pagy
- RSpec + WebMock + VCR (real cassettes, no mocks)
- Sentry, Brakeman, bundler-audit, Lefthook pre-commit
- machina-auth — optional SSO layer (see Authentication)
- A production
Dockerfile, a CI workflow, ajustfiletask runner,mise.tomlfor tool versions, and a wired/uphealth check
- Deploy infrastructure — this template is infra-agnostic. See
docs/DEPLOYMENT.mdfor what a production setup needs and bring your own (Kamal, Fly, Render, ECS, k8s, …). - pgvector / MCP server / media pipeline — opt-in per app; these are domain-specific and left out of the base template.
- Ruby 4.0.1 — install via mise
(
mise use --global ruby@4.0.1) - A recent
railsgem on PATH to exec the template. The generatedGemfilepins edge Rails (github: "rails/rails"), which bundle installs afterward. - PostgreSQL client libraries for the
pggem's C extension (brew install libpqon macOS). Dev runs on SQLite, so this only matters forbundle installand production.
The template is non-interactive by design — it's built to be driven by a Claude Code agent or any scripted/CI environment. Two inputs:
MACHINA_PORT— required. The port forbin/devand the OAuth callback. Pick a free port (3000 is almost always taken):ruby -rsocket -e 'TCPServer.new(3042).close' && echo free
- The app name — the standard
rails new <name>argument.
MACHINA_PORT=3042 rails new myapp \
-m ~/rails-template/template.rb \
--skip-test \
--skip-gitFlag notes:
--skip-test— recommended; we use RSpec, so Minitest scaffolding is cruft.--skip-git— recommended; the template does its owngit init+ initial commit.- Do not pass
--skip-bundle— the template'safter_bundlehook runsrspec:install,tailwindcss:install,db:prepare, and the initial commit.
The template aborts with a clear message if MACHINA_PORT is missing/invalid.
cd myapp
cp .env.example .env.local # fill in what you need (see below)
bin/setup # bundle install + db:prepare
bin/dev # web + css + worker on $MACHINA_PORTEnvironment variables (all optional unless you use the feature):
MACHINA_ORG_DOMAIN=example.com # org email domain for User#org_member?
MACHINA_PRODUCT_ID=... # only if you keep machina-auth
MACHINA_SERVICE_TOKEN=... # only if you keep machina-auth
MACHINA_IDENTITY_URL=... # your identity service (SSO)
OPENROUTER_API_KEY=... # only for the LLM stack
SENTRY_DSN=... # only for error tracking
The generated app wires up machina-auth, a public gem on rubygems.org
that implements SSO against a Machina Console identity service (configured via
MACHINA_IDENTITY_URL). It's the standard ZAR internal auth layer, included
here as a working reference — but it only talks to a compatible identity
service, so for most public users it's a starting point to replace, not
keep as-is.
The auth layer is intentionally isolated so it's easy to swap. To replace it:
- Delete
app/controllers/auth_controller.rb, the auth routes inconfig/routes.rb,config/initializers/machina.rb, and the OAuth bits ofapp/models/user.rb. - Remove
gem "machina-auth"from theGemfileand drop theMachina::ControllerHelpersinclude +before_action :authenticate!fromApplicationController. - Add Rails 8's built-in authentication generator, Devise, or OmniAuth.
Or, if you run your own compatible identity service, just point
MACHINA_IDENTITY_URL at it and keep everything.
Infra-agnostic on purpose. docs/DEPLOYMENT.md documents
the production contract (Docker image, Postgres, web + worker processes,
secrets, /up health check, object storage) and how the internal ZAR version
wires it up with AWS CDK, so you can reproduce the shape on any platform.
Two places to change things — not interchangeable:
| Want to change… | Edit… |
|---|---|
| Gemfile, routes, Procfile.dev, .env.example, the User migration | template.rb (generated inline via heredocs) |
| Anything else — app/, bin/, config/, spec/, .github/, Dockerfile, CLAUDE.md, mise.toml, justfile, lefthook.yml, .claude/ | files/ (copied verbatim at generation time) |
| Substitution rules or validation/abort behavior | template.rb |
Files in files/ use literal placeholder tokens that template.rb rewrites in
a single pass after copying:
| Token | Replaced with | Example |
|---|---|---|
APPNAME |
app name (snake_case) | myapp |
APPNAME_CLASS |
PascalCase | Myapp |
APPNAME_DASHED |
dashed | my-app |
APPNAME_PORT |
ENV["MACHINA_PORT"] |
3042 |
APPNAME_DOMAIN |
ENV["MACHINA_ORG_DOMAIN"] (default example.com) |
example.com |
Most-specific tokens are replaced first (APPNAME_CLASS before bare
APPNAME). There is no ERB substitution — the token approach avoids escaping
headaches in JSON/YAML/shell files.
After editing files/ or template.rb, dry-run the substitution without
running the full (~10 min edge-Rails bundle) rails new:
ruby script/smoke_test.rbIt copies files/ to a tempdir, performs the rename + substitution exactly as
template.rb does, checks for leftover APPNAME tokens, and parses every
generated .rb and YAML file. Exits 0 on success, 1 on any failure.
MIT — see LICENSE. Copyright (c) 2026 ZAR / Obie Fernandez.