Starting a new Solidus store involves only a few tweaks to support my preferred JS bundler, as well as a number of decisions regarding hosting, databases, assets and more. This is the sequence of steps I follow to launch a fresh store.
For the purposes of this demo, I have intentionally left out some of the details regarding configuring pipelines in Heroku, and distinct public/private S3 buckets in AWS.
Please read the documentation I’ve provided in those sections to ensure you are complying with best practices and procedures to keep your app safe and secure.
Before you get started, make sure to have Node, Docker, and the Heroku CLI installed. You will also need to set up an account on Heroku for deployment, and an AWS account for asset storage.
Check the Solidus releases page to determine which versions of Rails the latest release supports. At the time of writing Solidus supports Rails 8.1, so that’s what we’ll use here.
gem install rails -v 8.1.4Generate the application, passing the full version you just installed:
rails _8.1.4_ new your-new-store -T --database=postgresql --javascript=esbuild --skip-action-cable --skip-kamal --skip-rubocop --skip-brakeman --skip-solidWe use esbuild to bundle our JavaScript because it’s fast, lightweight, and supported by modern versions of Rails.
Solidus doesn’t make use of many of the components we skipped above. If you decide you want any of them later, they can easily be added back.
Confirm the generated application is on the Rails version you selected above.
Your Gemfile should specify that version, and the config.load_defaults call
in config/application.rb should match it. If rails new ran under a
different version than you intended, this is where you will find out.
As documented in the Solidus repository, create the asset manifest file:
cd your-new-store
mkdir -p app/assets/config
cat <<MANIFEST > app/assets/config/manifest.js
//= link_tree ../images
//= link_directory ../stylesheets .css
MANIFESTFor simplicity, we’ll run PostgreSQL locally using Docker. Create a
docker-compose.yml file in the app’s root directory with the following contents:
services:
db:
image: postgres:18.1-alpine3.22
environment:
POSTGRES_PASSWORD: postgres
volumes:
- postgres-data:/var/lib/postgresql/data
ports:
- "4300:5432"
volumes:
postgres-data:database.ymlUpdate config/database.yml so the port can be configured via an environment
variable. Add the following to the default configuration:
port: <%= ENV.fetch("DATABASE_PORT") { 4300 } %>
host: localhost
username: postgres
password: postgresThe host setting is important. Without it, Rails attempts to connect over a
Unix socket, which isn’t configured by default in this setup.
Add the dotenv gem to the development and test groups in your Gemfile,
then create a .env file containing:
DATABASE_PORT=4300Start PostgreSQL:
docker compose up -dFrom your Docker dashboard, you can verify that the container is running.
bundle add solidus
rails generate solidus:installI choose all the defaults except the payment platform setup; I choose Stripe.
You will be asked if you want to override a few autogenerated files from your previous rails new call, in order to utilize the Solidus Starter Frontend. Do it.
I generally choose Stripe during the installation process because it’s straightforward to set up. After the installer completes, add Stripe’s JavaScript package:
yarn add @stripe/stripe-jsFollow the setup instructions for solidus_stripe, to configure the Stripe integration.
A few adjustments are needed in order to use my preferred JS bundler, esbuild.
application.js import pathUpdate the application.js import path to include ./:
import "./controllers"Run:
bundle exec rails stimulus:manifest:updateThe underlying issue is that Solidus assumes Import Maps are installed, but Import Maps aren’t included by default in Rails 8 applications using esbuild.
Remove the following line from
app/assets/config/solidus_storefront_manifest.js:
//= link_tree ../../javascript .jsOtherwise, Sprockets attempts to link application.js twice, resulting in
duplicate asset errors and potentially interfering with asset bundling.
For additional background, see Migrating from Import Maps to esbuild in Rails.
In app/views/layouts/storefront.html.erb, replace the default Import Maps
JavaScript tag <%= javascript_importmap_tags %> with:
<%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>Ensure you are using a recent version of Ruby in your Gemfile.
ruby "4.0.7"At this point, you should be able to start the local development server by running:
bin/rails serverTo automatically run migrations during deployments, create a Procfile at the
root of the repository:
web: bundle exec puma -C config/puma.rb
release: bundle exec rails db:migrateYou can find these in the Settings tab in the Heroku dashboard for your app.
Set the RAILS_MASTER_KEY environment variable in Heroku so the application
can decrypt encrypted credentials. You can find this value in
config/master.key, which is ignored by Git by default.
Set the RAILS_ENV=production on the Heroku app. This ensures production
configuration options are set.
Add the Heroku Postgres add-on if it doesn’t already exist. This
automatically sets DATABASE_URL in the Heroku ENV variables, which Rails
uses for database connections.
If you are seeing Database Connection Errors when attempting to deploy, you may
need to override the production configuration in database.yml with the
following:
production:
<<: *default
url: <%= ENV['DATABASE_URL'] %>Start with a single environment and deploy directly to production. Before officially launching your store, use this documentation to set up your deployment pipeline, including a staging environment.
Follow the directions provided by Heroku in the Deploy tab for your new
app to push your Solidus application to Heroku.
git add .
git commit -m "Initial commit"
git push heroku mainIf the migrations don’t run automatically, run:
heroku run "bundle exec rails db:migrate" --app your-new-storeSee this documentation for additional assistance setting up the Heroku remote in your application’s local git repository.
If your build fails with:
SassC::SyntaxError: Error: Function rgb is missing argument $green.Add the following to config/application.rb:
config.assets.css_compressor = nilSet the ADMIN_EMAIL and ADMIN_PASSWORD Heroku Config Vars to your preferred
admin login email and secure password. This will be the login credentials you
can use to login at the /admin route.
After these Config Vars are set, run:
heroku run "bundle exec rails db:seed" --app <your-app-name>Add the S3 SDK to your Gemfile:
gem "aws-sdk-s3", require: falseBundle and commit the changes.
Create the bucket first, so that the permissions in the next step can name it directly.
Create a single bucket for now, named something like your-own-bucket. Always
split this into public and private buckets for each production, staging, and
development environment for real production applications.
For simplicity of this demo, I am making the bucket publicly accessible.
Please follow these best practices to ensure your buckets are secure when deploying your store.
Create an IAM group for the store, and attach a policy that grants access to only the bucket you just created:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListStoreBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::your-own-bucket"
},
{
"Sid": "ManageStoreObjects",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:PutObjectAcl",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::your-own-bucket/*"
}
]
}Create an IAM user for the application, adding it to the group you just created as part of the creation process. The user inherits the bucket permissions from the group, so there’s no policy to attach directly.
When creating access keys, select Application running outside AWS. Store the resulting access keys in Rails encrypted credentials.
Uncomment the Amazon configuration in config/storage.yml, and make sure the details match your bucket’s details:
amazon:
service: S3
access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
region: us-east-1
bucket: your-own-bucketUpdate config/environments/production.rb:
config.active_storage.service = :amazonLeave config/environments/development.rb configured to use local storage:
config.active_storage.service = :local