Scaffolder Backend Module Rails adds Rails support to Backstage Scaffolder. It lets your templates create a new Rails app with the defaults your team needs. You keep using Scaffolder. This module provides a Rails action so your template can call rails new behind the scenes.
At a high level it fits into the flow you already know. A template can fetch a base, generate a Rails app with your chosen options, write catalog info, publish a repo, then register the service in the catalog. Inputs can cover common choices like minimal setup, API only mode, database selection, skipping bundle, or using a custom template file. The result is a working Rails project that follows your standards from the first commit.
Typical use cases include spinning up a new Rails service, creating an API only app for a backend, or bootstrapping a service with a predefined structure. It also helps platform teams define one way to start a Rails project so every service begins with the same layout, linting, and dependencies. You can run it where Rails is available locally. You can also run it in a container image as part of your template. This reduces friction for teams and makes new service creation fast and repeatable.
Installation Instructions
These instructions apply to self-hosted Backstage only.
Install the backend module
- Add the package to your backend
yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-rails
- Wire it in with the new backend system
Edit packages/backend/src/index.ts
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
backend.add(import('@backstage/plugin-scaffolder-backend'));
backend.add(import('@backstage/plugin-scaffolder-backend-module-rails'));
backend.start();
- Wire it in with the old backend system
Edit packages/backend/src/plugins/scaffolder.ts
// packages/backend/src/plugins/scaffolder.ts
import { Router } from 'express';
import { CatalogClient } from '@backstage/catalog-client';
import {
createRouter,
createBuiltinActions,
DockerContainerRunner,
} from '@backstage/plugin-scaffolder-backend';
import { ScmIntegrations } from '@backstage/integration';
import { createFetchRailsAction } from '@backstage/plugin-scaffolder-backend-module-rails';
import { PluginEnvironment } from '../types';
export default async function createPlugin(env: PluginEnvironment): Promise<Router> {
const { logger, config, database, reader, discovery } = env;
const catalogClient = new CatalogClient({ discoveryApi: discovery });
const integrations = ScmIntegrations.fromConfig(config);
const containerRunner = new DockerContainerRunner({ logger });
const actions = [
...createBuiltinActions({
integrations,
config,
reader,
catalogClient,
containerRunner,
logger,
}),
createFetchRailsAction({
logger,
containerRunner,
}),
];
return await createRouter({
logger,
config,
database,
reader,
catalogClient,
actions,
});
}
If you do not plan to use Docker, you can pass undefined for containerRunner in both places above
Add a template that uses the action
Place a Template YAML in your software templates location. Many apps keep them under a folder named templates or similar, then register them in the catalog
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: rails-demo
title: Rails template
description: scaffolder Rails app
spec:
owner: backstage/techdocs-core
type: service
parameters:
- title: Fill in some steps
required:
- name
- owner
properties:
name:
title: Name
type: string
description: Unique name of the component
ui:autofocus: true
ui:options:
rows: 5
owner:
title: Owner
type: string
description: Owner of the component
ui:field: OwnerPicker
ui:options:
catalogFilter:
kind: Group
system:
title: System
type: string
description: System of the component
ui:field: EntityPicker
ui:options:
catalogFilter:
kind: System
defaultKind: System
- title: Choose a location
required:
- repoUrl
- dryRun
properties:
repoUrl:
title: Repository Location
type: string
ui:field: RepoUrlPicker
ui:options:
allowedHosts:
- github.com
dryRun:
title: Only perform a dry run, don't publish anything
type: boolean
default: false
railsArguments:
title: arguments to run the rails new command
type: object
properties:
minimal:
title: minimal
description: Preconfigure a minimal rails app
type: boolean
skipBundle:
title: skipBundle
description: Don't run bundle install
type: boolean
skipWebpackInstall:
title: skipWebpackInstall
description: Don't run Webpack install
type: boolean
api:
title: api
description: Preconfigure smaller stack for API only apps
type: boolean
template:
title: template
description: Path to some application template can be a filesystem path or URL
type: string
default: './rails-template-file.rb'
webpacker:
title: webpacker
description:
Preconfigure Webpack with a particular framework options react
vue
angular
elm
stimulus
type: string
enum:
- react
- vue
- angular
- elm
- stimulus
database:
title: database
description: Preconfigure for selected database options mysql postgresql sqlite3 oracle sqlserver jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc
type: string
enum:
- mysql
- postgresql
- sqlite3
- oracle
- sqlserver
- jdbcmysql
- jdbcsqlite3
- jdbcpostgresql
- jdbc
railsVersion:
title: Rails version in Gemfile
description:
Set up the application with Gemfile pointing to a specific version
options dev
edge
master
type: string
enum:
- dev
- edge
- master
steps:
- id: fetch-base
name: Fetch Base
action: fetch:rails
input:
url: ./template
values:
name: ${{ parameters.name }}
owner: ${{ parameters.owner }}
system: ${{ parameters.system }}
railsArguments: ${{ parameters.railsArguments }}
- name: Write Catalog information
action: catalog:write
input:
component:
apiVersion: 'backstage.io/v1alpha1'
kind: Component
metadata:
name: ${{ parameters.name }}
annotations:
github.com/project-slug: ${{ parameters.repoUrl | projectSlug }}
spec:
type: service
lifecycle: production
owner: ${{ parameters.owner }}
- id: publish
if: ${{ parameters.dryRun !== true }}
name: Publish
action: publish:github
input:
allowedHosts:
- github.com
description: This is ${{ parameters.name }}
repoUrl: ${{ parameters.repoUrl }}
- id: register
if: ${{ parameters.dryRun !== true }}
name: Register
action: catalog:register
input:
repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
catalogInfoPath: '/catalog-info.yaml'
- name: Results
if: ${{ parameters.dryRun }}
action: debug:log
input:
listWorkspace: true
output:
links:
- title: Repository
url: ${{ steps['publish'].output.remoteUrl }}
- title: Open in catalog
icon: catalog
entityRef: ${{ steps['register'].output.entityRef }}
Choose the runtime for rails new
The action runs the rails new command. Your runner needs a rails install or you can run it in a container
If your runner has rails on the path no change is needed
If you want to run inside a container set imageName in the step
steps:
- id: fetch-base
name: Fetch Base
action: fetch:rails
input:
url: ./template
imageName: repository/rails:tag
values:
name: ${{ parameters.name }}
owner: ${{ parameters.owner }}
system: ${{ parameters.system }}
railsArguments: ${{ parameters.railsArguments }}
Where you will see it in the app
This module does not add a frontend component. The Create page in your app will list any Template you register in the catalog. Use the Template above in your catalog so the action shows up there
Changelog
This changelog is produced from commits made to the Scaffolder Backend Module Rails plugin since a year ago, and based on the code located here. It may not contain information about all commits. Releases and version bumps are intentionally omitted. This changelog is generated by AI.
Breaking changes
- Remove old deprecated scaffolder types. If you rely on those types in custom code you need to migrate. #31235 Merged 12 days ago
- Raise Node runtime to 20 and 22. Drop support for 18. #27286 Merged 11 months ago
Improvements
- Migrate the Rails module actions to the new actions format. Existing templates should keep working. If you register actions by hand update to the new format. #30134 Merged 4 months ago
- Promote backend feature exports to the main entry point. You no longer need alpha imports for features in this area. #27088 Merged 11 months ago
Deprecations
- Deprecate createTemplateAction with the old JSONSchema and zod shapes. Use the new actions API. #28798 Merged 7 months ago
Dependencies
- Bump TypeScript to 5.6. #29090 Merged 7 months ago
- Update Ruby Docker tag to 3.4. #28548 Merged 7 months ago
Internal
- Remove most backend common usages in scaffolder code. #29017 Merged 7 months ago
Set up Backstage in minutes with Roadie
Focus on using Backstage, rather than building and maintaining it.