dist/codex/spree-commerce/skills/spree-deployment/SKILL.md
Deploy Spree to production — PostgreSQL + Redis + Sidekiq stack, Docker multi-arch images on GHCR, the `spree-starter` Dockerfile + Compose, Heroku/Render/Fly.io/AWS targets, env-var conventions, RAILS_MASTER_KEY, asset precompilation (Tailwind 4 + Propshaft), Action Cable, MeiliSearch indexing, S3 / ActiveStorage for media, log/observability setup, zero-downtime deploys, and migration strategy. Use when going from local dev to production, scaling Spree, or troubleshooting deploys.
npx skillsauth add orcaqubits/agentic-commerce-claude-plugins spree-deploymentInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fetch live docs:
spree-starter Dockerfile and docker-compose.yml for the current production stack.| Tier | Component | |------|-----------| | Web | Puma serving the Rails app (Spree API + admin) | | Worker | Sidekiq processing background jobs | | DB | PostgreSQL 14+ (recommended) or MySQL 8 | | Cache + Jobs | Redis 7+ | | Search | MeiliSearch (v5.4+) or Algolia or Postgres FTS | | Media | S3-compatible (ActiveStorage) | | Email | SMTP (SendGrid, Postmark, SES) | | CDN | CloudFront, Cloudflare, or platform-native | | Front-end (headless) | Vercel for Next.js storefront | | Observability | Sentry, Datadog, New Relic, AppSignal |
Spree publishes multi-arch (ARM + x86) Docker images to GitHub Container Registry:
ghcr.io/spree/spree:5.4.2
ghcr.io/spree/spree-starter:latest
ghcr.io/spree/storefront:5.4.2
(Verify the exact image tags against the GHCR releases.)
spree-starter DockerfileThe starter ships a production-ready Dockerfile with:
bootsnap precompile for faster boot| Var | Purpose |
|-----|---------|
| DATABASE_URL | postgres://user:pass@host:5432/db |
| REDIS_URL | redis://host:6379/0 |
| RAILS_ENV | production |
| RAILS_MASTER_KEY | Decrypts config/credentials.yml.enc |
| SECRET_KEY_BASE | If not in credentials |
| RAILS_LOG_TO_STDOUT | true for containerized envs |
| RAILS_SERVE_STATIC_FILES | true if no CDN in front |
| WEB_CONCURRENCY | Puma worker count |
| RAILS_MAX_THREADS | Threads per Puma worker |
| MEILISEARCH_HOST, MEILISEARCH_API_KEY | Search backend |
| AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_BUCKET, AWS_REGION | ActiveStorage S3 |
| SMTP_* | Email |
| SENTRY_DSN | Error tracking |
| STRIPE_*, ADYEN_*, PAYPAL_* | Payment gateways |
| SPREE_API_URL | (Storefront-side) backend URL |
| NEXT_PUBLIC_SPREE_PUBLISHABLE_KEY | (Storefront-side) publishable key |
Spree v5 uses Propshaft + Tailwind CSS 4. In Dockerfile:
RUN bin/rails assets:precompile RAILS_ENV=production
For Tailwind, tailwindcss-rails watches files at runtime in dev; in production, precompile bakes the final CSS. If using esbuild, run bin/rails javascript:build too.
Run before traffic flips:
bin/rails db:migrate
For zero-downtime:
# config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
config.redis = { url: ENV['REDIS_URL'] }
end
Sidekiq.configure_client do |config|
config.redis = { url: ENV['REDIS_URL'] }
end
# config/sidekiq.yml
:concurrency: 10
:queues:
- critical
- default
- webhooks
- mailers
- low
Webhook delivery uses Sidekiq — without it, webhooks don't fire.
For real-time admin updates (e.g., live order list refresh), Action Cable runs on the same Puma process. Redis is the broadcast adapter.
After deploy:
bin/rails spree:search:reindex
(Verify the rake task name.) Run on a worker, not the web tier — long-running.
ActiveStorage::AnalyzeJob etc.; can fall behind on bulk uploadsconfig/credentials.yml.enc backed up alongside RAILS_MASTER_KEY (keep separate)heroku create my-spree
heroku addons:create heroku-postgresql:standard-0
heroku addons:create heroku-redis:premium-0
heroku addons:create sendgrid:starter
heroku buildpacks:add heroku/ruby
heroku buildpacks:add heroku/nodejs # for asset compilation
heroku config:set RAILS_MASTER_KEY=$(cat config/master.key)
heroku config:set RAILS_LOG_TO_STDOUT=true
heroku config:set RAILS_SERVE_STATIC_FILES=true
git push heroku main
heroku run bin/rails db:migrate
heroku run bin/rails spree:install
Run Sidekiq on a worker dyno:
heroku ps:scale web=2 worker=1
Procfile:
web: bundle exec puma -C config/puma.rb
worker: bundle exec sidekiq -C config/sidekiq.yml
release: bin/rails db:migrate
docker build -t spree:latest .
docker run -d --name spree-web \
-e DATABASE_URL=... \
-e REDIS_URL=... \
-e RAILS_MASTER_KEY=... \
-p 3000:3000 \
spree:latest
docker run -d --name spree-worker \
-e DATABASE_URL=... \
-e REDIS_URL=... \
-e RAILS_MASTER_KEY=... \
spree:latest bundle exec sidekiq
For Kubernetes, use separate Deployments for web and worker, plus a Job for db:migrate on each release.
Vercel:
Docker / self-hosted:
| Layer | What to cache |
|-------|---------------|
| CDN | Static assets (/assets/*, /packs/*) |
| Rails fragment cache | Product cards on PLP (busted by product.updated) |
| Rails low-level cache | Computed totals, settings |
| Database query cache | Per-request (default Rails behavior) |
| Sidekiq result cache | Webhook delivery dedup |
SIGTSTP then SIGTERM)/admin/login_check or similar low-overhead path/api/v3/* separate from adminRAILS_MASTER_KEY not set — app crashes at boot with cryptic error.RAILS_SERVE_STATIC_FILES=true without a CDN — 404s on assets.db:migrate on release — runtime errors.tmp/, log/, node_modules/ from slug.Always cross-reference your platform's current Rails 7+ guide and the spree-starter repo's deployment notes — both move with each Spree release.
development
Build with Spree's headless Next.js storefront — the official `spree/storefront` repo (Next.js 16 App Router with Server Actions and Turbopack, React 19 Server Components, Tailwind CSS 4, TypeScript 5, `@spree/sdk`, Sentry), server-only auth (httpOnly JWT cookies + publishable key), MeiliSearch faceted catalog, one-page checkout with Apple/Google Pay/Klarna/Affirm/SEPA, multi-region market routing, GA4 + JSON-LD SEO, and Vercel/Docker deployment. Use when forking or customizing the storefront, or evaluating headless adoption.
tools
Build Spree extensions as Rails engines — gem scaffolding, `bin/rails g spree:extension`, mounting routes/migrations/assets, the modern `prepend` decorator pattern (`*_decorator.rb` with `self.prepended(base)`), generators (`spree:model_decorator`, `spree:controller_decorator`), the four customization surfaces in preference order (Events > Webhooks > Dependencies > Decorators), Spree::Dependencies for swapping service objects, gem release/versioning, and the deprecated Deface engine. Use when building a reusable Spree extension or adding non-trivial customization to an app.
development
Build with Spree's event bus and Webhooks 2.0 — `Spree::Events` publication, `Spree::Subscriber` DSL with `subscribes_to` and `on`, wildcard matching, lifecycle events (`{model}.created/.updated/.deleted` via `publishes_lifecycle_events`), the canonical event catalog (order.*, payment.*, shipment.*, product.*), Webhooks 2.0 endpoints, HMAC-SHA256 signing (`X-Spree-Webhook-Signature`), exponential-backoff retries, and Sidekiq job orchestration. Use when wiring event-driven business logic, building webhook consumers, or replacing ActiveSupport callback chains.
tools
Cross-cutting Spree development patterns — the customization preference hierarchy (Events > Webhooks > Dependencies > Decorators), `Spree::Dependencies` service-object swapping, the `_decorator.rb` + `prepend` + `self.prepended` idiom, idempotent subscribers and webhook receivers, multi-store scoping discipline, prefixed IDs, calculator polymorphism (shipping/promotion/tax share the base), service-object composition with `dry-monads` or simple results, why to avoid `class_eval` reopening and Deface, and Spree-on-Rails idioms (Hotwire/Turbo Stimulus, ActiveStorage, Action Cable, Sidekiq). Use when designing the architecture of a Spree extension or solving cross-cutting concerns.